diff --git a/NEWS.rst b/NEWS.rst
index 2562fa3..05ac943 100644
--- a/NEWS.rst
+++ b/NEWS.rst
@@ -12,8 +12,9 @@
0.6.3 - **Not released yet**
============================
-* MOVE requests fixed
-* Faster REPORT answers
+* MOVE requests fixed
+* Faster REPORT answers
+* Executable put in the package
0.6.2 - Seeds
diff --git a/bin/radicale b/bin/radicale
new file mode 100755
index 0000000..1d41442
--- /dev/null
+++ b/bin/radicale
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# This file is part of Radicale Server - Calendar Server
+# Copyright © 2008-2011 Guillaume Ayoub
+# Copyright © 2008 Nicolas Kandel
+# Copyright © 2008 Pascal Halter
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Radicale. If not, see .
+
+"""
+Radicale CalDAV Server.
+
+Launch the server according to configuration and command-line options.
+
+"""
+
+import radicale.__main__
+
+
+radicale.__main__.run()
diff --git a/radicale.py b/radicale.py
index 34e693f..1d41442 100755
--- a/radicale.py
+++ b/radicale.py
@@ -19,12 +19,6 @@
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see .
-# This file is just a script, allow [a-z0-9]* variable names
-# pylint: disable-msg=C0103
-
-# ``import radicale`` refers to the ``radicale`` module, not ``radicale.py``
-# pylint: disable-msg=W0406
-
"""
Radicale CalDAV Server.
@@ -32,139 +26,7 @@ Launch the server according to configuration and command-line options.
"""
-import atexit
-import os
-import sys
-import optparse
-import signal
-import threading
-from wsgiref.simple_server import make_server
+import radicale.__main__
-import radicale
-# Get command-line options
-parser = optparse.OptionParser(version=radicale.VERSION)
-parser.add_option(
- "-d", "--daemon", action="store_true",
- default=radicale.config.getboolean("server", "daemon"),
- help="launch as daemon")
-parser.add_option(
- "-p", "--pid",
- default=radicale.config.get("server", "pid"),
- help="set PID filename for daemon mode")
-parser.add_option(
- "-f", "--foreground", action="store_false", dest="daemon",
- help="launch in foreground (opposite of --daemon)")
-parser.add_option(
- "-H", "--hosts",
- default=radicale.config.get("server", "hosts"),
- help="set server hostnames and ports")
-parser.add_option(
- "-s", "--ssl", action="store_true",
- default=radicale.config.getboolean("server", "ssl"),
- help="use SSL connection")
-parser.add_option(
- "-S", "--no-ssl", action="store_false", dest="ssl",
- help="do not use SSL connection (opposite of --ssl)")
-parser.add_option(
- "-k", "--key",
- default=radicale.config.get("server", "key"),
- help="set private key file")
-parser.add_option(
- "-c", "--certificate",
- default=radicale.config.get("server", "certificate"),
- help="set certificate file")
-parser.add_option(
- "-D", "--debug", action="store_true",
- default=radicale.config.getboolean("logging", "debug"),
- help="print debug information")
-options = parser.parse_args()[0]
-
-# Update Radicale configuration according to options
-for option in parser.option_list:
- key = option.dest
- if key:
- section = "logging" if key == "debug" else "server"
- value = getattr(options, key)
- radicale.config.set(section, key, str(value))
-
-# Start logging
-radicale.log.start()
-
-# Fork if Radicale is launched as daemon
-if options.daemon:
- pid = os.fork()
- if pid:
- try:
- if options.pid:
- open(options.pid, 'w').write(str(pid))
- finally:
- sys.exit()
- sys.stdout = sys.stderr = open(os.devnull, "w")
-
-# Register exit function
-def cleanup():
- radicale.log.LOGGER.debug("Cleaning up")
- # Remove PID file
- if options.pid and options.daemon:
- os.unlink(options.pid)
-
-atexit.register(cleanup)
-radicale.log.LOGGER.info("Starting Radicale")
-
-# Create calendar servers
-servers = []
-server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
-shutdown_program = threading.Event()
-
-for host in options.hosts.split(','):
- address, port = host.strip().rsplit(':', 1)
- address, port = address.strip('[] '), int(port)
- servers.append(
- make_server(address, port, radicale.Application(),
- server_class, radicale.RequestHandler))
-
-# SIGTERM and SIGINT (aka KeyboardInterrupt) should just mark this for shutdown
-signal.signal(signal.SIGTERM, lambda *_: shutdown_program.set())
-signal.signal(signal.SIGINT, lambda *_: shutdown_program.set())
-
-def serve_forever(server):
- """Serve a server forever, cleanly shutdown when things go wrong."""
- try:
- server.serve_forever()
- finally:
- shutdown_program.set()
-
-# Start the servers in a different loop to avoid possible race-conditions, when
-# a server exists but another server is added to the list at the same time
-for server in servers:
- radicale.log.LOGGER.debug(
- "Listening to %s port %s" % (server.server_name, server.server_port))
- if options.ssl:
- radicale.log.LOGGER.debug("Using SSL")
- threading.Thread(target=serve_forever, args=(server,)).start()
-
-radicale.log.LOGGER.debug("Radicale server ready")
-
-# Main loop: wait until all servers are exited
-try:
- # We must do the busy-waiting here, as all ``.join()`` calls completly
- # block the thread, such that signals are not received
- while True:
- # The number is irrelevant, it only needs to be greater than 0.05 due
- # to python implementing its own busy-waiting logic
- shutdown_program.wait(5.0)
- if shutdown_program.is_set():
- break
-finally:
- # Ignore signals, so that they cannot interfere
- signal.signal(signal.SIGINT, signal.SIG_IGN)
- signal.signal(signal.SIGTERM, signal.SIG_IGN)
-
- radicale.log.LOGGER.info("Stopping Radicale")
-
- for server in servers:
- radicale.log.LOGGER.debug(
- "Closing server listening to %s port %s" % (
- server.server_name, server.server_port))
- server.shutdown()
+radicale.__main__.run()
diff --git a/radicale/__main__.py b/radicale/__main__.py
new file mode 100644
index 0000000..ef3f769
--- /dev/null
+++ b/radicale/__main__.py
@@ -0,0 +1,173 @@
+# -*- coding: utf-8 -*-
+#
+# This file is part of Radicale Server - Calendar Server
+# Copyright © 2011 Guillaume Ayoub
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Radicale. If not, see .
+
+"""
+Radicale executable module.
+
+This module can be executed from a command line with ``$python -m radicale`` or
+from a python programme with ``radicale.__main__.run()``.
+
+"""
+
+import atexit
+import os
+import sys
+import optparse
+import signal
+import threading
+from wsgiref.simple_server import make_server
+
+import radicale
+
+
+def run():
+ """Run Radicale as a standalone server."""
+ # Get command-line options
+ parser = optparse.OptionParser(version=radicale.VERSION)
+ parser.add_option(
+ "-d", "--daemon", action="store_true",
+ default=radicale.config.getboolean("server", "daemon"),
+ help="launch as daemon")
+ parser.add_option(
+ "-p", "--pid",
+ default=radicale.config.get("server", "pid"),
+ help="set PID filename for daemon mode")
+ parser.add_option(
+ "-f", "--foreground", action="store_false", dest="daemon",
+ help="launch in foreground (opposite of --daemon)")
+ parser.add_option(
+ "-H", "--hosts",
+ default=radicale.config.get("server", "hosts"),
+ help="set server hostnames and ports")
+ parser.add_option(
+ "-s", "--ssl", action="store_true",
+ default=radicale.config.getboolean("server", "ssl"),
+ help="use SSL connection")
+ parser.add_option(
+ "-S", "--no-ssl", action="store_false", dest="ssl",
+ help="do not use SSL connection (opposite of --ssl)")
+ parser.add_option(
+ "-k", "--key",
+ default=radicale.config.get("server", "key"),
+ help="set private key file")
+ parser.add_option(
+ "-c", "--certificate",
+ default=radicale.config.get("server", "certificate"),
+ help="set certificate file")
+ parser.add_option(
+ "-D", "--debug", action="store_true",
+ default=radicale.config.getboolean("logging", "debug"),
+ help="print debug information")
+ options = parser.parse_args()[0]
+
+ # Update Radicale configuration according to options
+ for option in parser.option_list:
+ key = option.dest
+ if key:
+ section = "logging" if key == "debug" else "server"
+ value = getattr(options, key)
+ radicale.config.set(section, key, str(value))
+
+ # Start logging
+ radicale.log.start()
+
+ # Fork if Radicale is launched as daemon
+ if options.daemon:
+ pid = os.fork()
+ if pid:
+ try:
+ if options.pid:
+ open(options.pid, 'w').write(str(pid))
+ finally:
+ sys.exit()
+ sys.stdout = sys.stderr = open(os.devnull, "w")
+
+ # Register exit function
+ def cleanup():
+ """Remove the PID files."""
+ radicale.log.LOGGER.debug("Cleaning up")
+ # Remove PID file
+ if options.pid and options.daemon:
+ os.unlink(options.pid)
+
+ atexit.register(cleanup)
+ radicale.log.LOGGER.info("Starting Radicale")
+
+ # Create calendar servers
+ servers = []
+ server_class = radicale.HTTPSServer if options.ssl else radicale.HTTPServer
+ shutdown_program = threading.Event()
+
+ for host in options.hosts.split(','):
+ address, port = host.strip().rsplit(':', 1)
+ address, port = address.strip('[] '), int(port)
+ servers.append(
+ make_server(address, port, radicale.Application(),
+ server_class, radicale.RequestHandler))
+
+ # SIGTERM and SIGINT (aka KeyboardInterrupt) should just mark this for
+ # shutdown
+ signal.signal(signal.SIGTERM, lambda *_: shutdown_program.set())
+ signal.signal(signal.SIGINT, lambda *_: shutdown_program.set())
+
+ def serve_forever(server):
+ """Serve a server forever, cleanly shutdown when things go wrong."""
+ try:
+ server.serve_forever()
+ finally:
+ shutdown_program.set()
+
+ # Start the servers in a different loop to avoid possible race-conditions,
+ # when a server exists but another server is added to the list at the same
+ # time
+ for server in servers:
+ radicale.log.LOGGER.debug(
+ "Listening to %s port %s" % (
+ server.server_name, server.server_port))
+ if options.ssl:
+ radicale.log.LOGGER.debug("Using SSL")
+ threading.Thread(target=serve_forever, args=(server,)).start()
+
+ radicale.log.LOGGER.debug("Radicale server ready")
+
+ # Main loop: wait until all servers are exited
+ try:
+ # We must do the busy-waiting here, as all ``.join()`` calls completly
+ # block the thread, such that signals are not received
+ while True:
+ # The number is irrelevant, it only needs to be greater than 0.05
+ # due to python implementing its own busy-waiting logic
+ shutdown_program.wait(5.0)
+ if shutdown_program.is_set():
+ break
+ finally:
+ # Ignore signals, so that they cannot interfere
+ signal.signal(signal.SIGINT, signal.SIG_IGN)
+ signal.signal(signal.SIGTERM, signal.SIG_IGN)
+
+ radicale.log.LOGGER.info("Stopping Radicale")
+
+ for server in servers:
+ radicale.log.LOGGER.debug(
+ "Closing server listening to %s port %s" % (
+ server.server_name, server.server_port))
+ server.shutdown()
+
+
+if __name__ == '__main__':
+ run()
diff --git a/setup.py b/setup.py
index 3a6a8f3..c8eb956 100755
--- a/setup.py
+++ b/setup.py
@@ -36,27 +36,11 @@ For further information, please visit the `Radicale Website
"""
-import os
from distutils.core import setup
-from distutils.command.build_scripts import build_scripts
import radicale
-# build_scripts is known to have a lot of public methods
-# pylint: disable=R0904
-class BuildScripts(build_scripts):
- """Build the package."""
- def run(self):
- """Run building."""
- # These lines remove the .py extension from the radicale executable
- self.mkpath(self.build_dir)
- for script in self.scripts:
- root, _ = os.path.splitext(script)
- self.copy_file(script, os.path.join(self.build_dir, root))
-# pylint: enable=R0904
-
-
# When the version is updated, ``radicale.VERSION`` must be modified.
# A new section in the ``NEWS`` file must be added too.
setup(
@@ -73,8 +57,7 @@ setup(
platforms="Any",
packages=["radicale", "radicale.acl"],
provides=["radicale"],
- scripts=["radicale.py"],
- cmdclass={"build_scripts": BuildScripts},
+ scripts=["bin/radicale"],
keywords=["calendar", "CalDAV"],
classifiers=[
"Development Status :: 4 - Beta",