__init__.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778
  1. # This file is part of Radicale - CalDAV and CardDAV server
  2. # Copyright © 2008 Nicolas Kandel
  3. # Copyright © 2008 Pascal Halter
  4. # Copyright © 2008-2017 Guillaume Ayoub
  5. # Copyright © 2017-2022 Unrud <unrud@outlook.com>
  6. # Copyright © 2024-2024 Peter Bieringer <pb@bieringer.de>
  7. #
  8. # This library is free software: you can redistribute it and/or modify
  9. # it under the terms of the GNU General Public License as published by
  10. # the Free Software Foundation, either version 3 of the License, or
  11. # (at your option) any later version.
  12. #
  13. # This library is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with Radicale. If not, see <http://www.gnu.org/licenses/>.
  20. """
  21. Entry point for external WSGI servers (like uWSGI or Gunicorn).
  22. Configuration files can be specified in the environment variable
  23. ``RADICALE_CONFIG``.
  24. """
  25. import os
  26. import threading
  27. from typing import Iterable, Optional, cast
  28. from radicale import config, log, types, utils
  29. from radicale.app import Application
  30. from radicale.log import logger
  31. VERSION: str = utils.package_version("radicale")
  32. _application_instance: Optional[Application] = None
  33. _application_config_path: Optional[str] = None
  34. _application_lock = threading.Lock()
  35. def _get_application_instance(config_path: str, wsgi_errors: types.ErrorStream
  36. ) -> Application:
  37. global _application_instance, _application_config_path
  38. with _application_lock:
  39. if _application_instance is None:
  40. log.setup()
  41. with log.register_stream(wsgi_errors):
  42. _application_config_path = config_path
  43. configuration = config.load(config.parse_compound_paths(
  44. config.DEFAULT_CONFIG_PATH,
  45. config_path))
  46. log.set_level(cast(str, configuration.get("logging", "level")), configuration.get("logging", "backtrace_on_debug"))
  47. # Log configuration after logger is configured
  48. default_config_active = True
  49. for source, miss in configuration.sources():
  50. logger.info("%s %s", "Skipped missing/unreadable" if miss
  51. else "Loaded", source)
  52. if not miss and source != "default config":
  53. default_config_active = False
  54. if default_config_active:
  55. logger.warning("%s", "No config file found/readable - only default config is active")
  56. _application_instance = Application(configuration)
  57. if _application_config_path != config_path:
  58. raise ValueError("RADICALE_CONFIG must not change: %r != %r" %
  59. (config_path, _application_config_path))
  60. return _application_instance
  61. def application(environ: types.WSGIEnviron,
  62. start_response: types.WSGIStartResponse) -> Iterable[bytes]:
  63. """Entry point for external WSGI servers."""
  64. config_path = environ.get("RADICALE_CONFIG",
  65. os.environ.get("RADICALE_CONFIG"))
  66. app = _get_application_instance(config_path, environ["wsgi.errors"])
  67. return app(environ, start_response)