require python>=3.5.2
This commit is contained in:
parent
59d10ef9f7
commit
a5fa35e785
@ -3,10 +3,6 @@ sudo: false
|
|||||||
|
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: linux
|
|
||||||
python: 3.3
|
|
||||||
- os: linux
|
|
||||||
python: 3.4
|
|
||||||
- os: linux
|
- os: linux
|
||||||
python: 3.5
|
python: 3.5
|
||||||
- os: linux
|
- os: linux
|
||||||
|
@ -73,10 +73,6 @@ class HTTPServer(wsgiref.simple_server.WSGIServer):
|
|||||||
self.server_close()
|
self.server_close()
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if self.client_timeout and sys.version_info < (3, 5, 2):
|
|
||||||
logger.warning("Using server.timeout with Python < 3.5.2 "
|
|
||||||
"can cause network connection failures")
|
|
||||||
|
|
||||||
def get_request(self):
|
def get_request(self):
|
||||||
# Set timeout for client
|
# Set timeout for client
|
||||||
_socket, address = super().get_request()
|
_socket, address = super().get_request()
|
||||||
@ -249,12 +245,8 @@ def serve(configuration):
|
|||||||
if configuration.getboolean("server", "ssl") else "")
|
if configuration.getboolean("server", "ssl") else "")
|
||||||
|
|
||||||
# Create a socket pair to notify the select syscall of program shutdown
|
# Create a socket pair to notify the select syscall of program shutdown
|
||||||
# This is not available in python < 3.5 on Windows
|
shutdown_program_socket_in, shutdown_program_socket_out = (
|
||||||
if hasattr(socket, "socketpair"):
|
socket.socketpair())
|
||||||
shutdown_program_socket_in, shutdown_program_socket_out = (
|
|
||||||
socket.socketpair())
|
|
||||||
else:
|
|
||||||
shutdown_program_socket_in, shutdown_program_socket_out = None, None
|
|
||||||
|
|
||||||
# SIGTERM and SIGINT (aka KeyboardInterrupt) should just mark this for
|
# SIGTERM and SIGINT (aka KeyboardInterrupt) should just mark this for
|
||||||
# shutdown
|
# shutdown
|
||||||
@ -265,18 +257,16 @@ def serve(configuration):
|
|||||||
return
|
return
|
||||||
logger.info("Stopping Radicale")
|
logger.info("Stopping Radicale")
|
||||||
shutdown_program = True
|
shutdown_program = True
|
||||||
if shutdown_program_socket_in:
|
shutdown_program_socket_in.sendall(b" ")
|
||||||
shutdown_program_socket_in.sendall(b"goodbye")
|
|
||||||
signal.signal(signal.SIGTERM, shutdown)
|
signal.signal(signal.SIGTERM, shutdown)
|
||||||
signal.signal(signal.SIGINT, shutdown)
|
signal.signal(signal.SIGINT, shutdown)
|
||||||
|
|
||||||
# Main loop: wait for requests on any of the servers or program shutdown
|
# Main loop: wait for requests on any of the servers or program shutdown
|
||||||
sockets = list(servers.keys())
|
sockets = list(servers.keys())
|
||||||
if shutdown_program_socket_out:
|
# Use socket pair to get notified of program shutdown
|
||||||
# Use socket pair to get notified of program shutdown
|
sockets.append(shutdown_program_socket_out)
|
||||||
sockets.append(shutdown_program_socket_out)
|
|
||||||
select_timeout = None
|
select_timeout = None
|
||||||
if not shutdown_program_socket_out or os.name == "nt":
|
if os.name == "nt":
|
||||||
# Fallback to busy waiting. (select.select blocks SIGINT on Windows.)
|
# Fallback to busy waiting. (select.select blocks SIGINT on Windows.)
|
||||||
select_timeout = 1.0
|
select_timeout = 1.0
|
||||||
logger.info("Radicale server ready")
|
logger.info("Radicale server ready")
|
||||||
|
@ -34,7 +34,6 @@ import pickle
|
|||||||
import posixpath
|
import posixpath
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
@ -47,12 +46,9 @@ from tempfile import NamedTemporaryFile, TemporaryDirectory
|
|||||||
|
|
||||||
import vobject
|
import vobject
|
||||||
|
|
||||||
|
from radicale import xmlutils
|
||||||
from radicale.log import logger
|
from radicale.log import logger
|
||||||
|
|
||||||
if sys.version_info >= (3, 5):
|
|
||||||
# HACK: Avoid import cycle for Python < 3.5
|
|
||||||
from radicale import xmlutils
|
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
import ctypes
|
import ctypes
|
||||||
import ctypes.wintypes
|
import ctypes.wintypes
|
||||||
@ -97,10 +93,6 @@ INTERNAL_TYPES = ("multifilesystem",)
|
|||||||
|
|
||||||
def load(configuration):
|
def load(configuration):
|
||||||
"""Load the storage manager chosen in configuration."""
|
"""Load the storage manager chosen in configuration."""
|
||||||
if sys.version_info < (3, 5):
|
|
||||||
# HACK: Avoid import cycle for Python < 3.5
|
|
||||||
global xmlutils
|
|
||||||
from radicale import xmlutils
|
|
||||||
storage_type = configuration.get("storage", "type")
|
storage_type = configuration.get("storage", "type")
|
||||||
if storage_type == "multifilesystem":
|
if storage_type == "multifilesystem":
|
||||||
collection_class = Collection
|
collection_class = Collection
|
||||||
@ -241,27 +233,6 @@ def find_available_name(exists_fn, suffix=""):
|
|||||||
raise RuntimeError("No unique random sequence found")
|
raise RuntimeError("No unique random sequence found")
|
||||||
|
|
||||||
|
|
||||||
def scandir(path, only_dirs=False, only_files=False):
|
|
||||||
"""Iterator for directory elements. (For compatibility with Python < 3.5)
|
|
||||||
|
|
||||||
``only_dirs`` only return directories
|
|
||||||
|
|
||||||
``only_files`` only return files
|
|
||||||
|
|
||||||
"""
|
|
||||||
if sys.version_info >= (3, 5):
|
|
||||||
for entry in os.scandir(path):
|
|
||||||
if ((not only_files or entry.is_file()) and
|
|
||||||
(not only_dirs or entry.is_dir())):
|
|
||||||
yield entry.name
|
|
||||||
else:
|
|
||||||
for name in os.listdir(path):
|
|
||||||
p = os.path.join(path, name)
|
|
||||||
if ((not only_files or os.path.isfile(p)) and
|
|
||||||
(not only_dirs or os.path.isdir(p))):
|
|
||||||
yield name
|
|
||||||
|
|
||||||
|
|
||||||
def get_etag(text):
|
def get_etag(text):
|
||||||
"""Etag from collection or item.
|
"""Etag from collection or item.
|
||||||
|
|
||||||
@ -354,7 +325,8 @@ def path_to_filesystem(root, *paths):
|
|||||||
# Check for conflicting files (e.g. case-insensitive file systems
|
# Check for conflicting files (e.g. case-insensitive file systems
|
||||||
# or short names on Windows file systems)
|
# or short names on Windows file systems)
|
||||||
if (os.path.lexists(safe_path) and
|
if (os.path.lexists(safe_path) and
|
||||||
part not in scandir(safe_path_parent)):
|
part not in (e.name for e in
|
||||||
|
os.scandir(safe_path_parent))):
|
||||||
raise CollidingPathError(part)
|
raise CollidingPathError(part)
|
||||||
return safe_path
|
return safe_path
|
||||||
|
|
||||||
@ -899,7 +871,10 @@ class Collection(BaseCollection):
|
|||||||
with child_context_manager(sane_path, href):
|
with child_context_manager(sane_path, href):
|
||||||
yield collection.get(href)
|
yield collection.get(href)
|
||||||
|
|
||||||
for href in scandir(filesystem_path, only_dirs=True):
|
for entry in os.scandir(filesystem_path):
|
||||||
|
if not entry.is_dir():
|
||||||
|
continue
|
||||||
|
href = entry.name
|
||||||
if not is_safe_filesystem_path_component(href):
|
if not is_safe_filesystem_path_component(href):
|
||||||
if not href.startswith(".Radicale"):
|
if not href.startswith(".Radicale"):
|
||||||
logger.debug("Skipping collection %r in %r",
|
logger.debug("Skipping collection %r in %r",
|
||||||
@ -1174,7 +1149,8 @@ class Collection(BaseCollection):
|
|||||||
history_folder = os.path.join(self._filesystem_path,
|
history_folder = os.path.join(self._filesystem_path,
|
||||||
".Radicale.cache", "history")
|
".Radicale.cache", "history")
|
||||||
try:
|
try:
|
||||||
for href in scandir(history_folder):
|
for entry in os.scandir(history_folder):
|
||||||
|
href = entry.name
|
||||||
if not is_safe_filesystem_path_component(href):
|
if not is_safe_filesystem_path_component(href):
|
||||||
continue
|
continue
|
||||||
if os.path.isfile(os.path.join(self._filesystem_path, href)):
|
if os.path.isfile(os.path.join(self._filesystem_path, href)):
|
||||||
@ -1285,7 +1261,10 @@ class Collection(BaseCollection):
|
|||||||
return token, changes
|
return token, changes
|
||||||
|
|
||||||
def list(self):
|
def list(self):
|
||||||
for href in scandir(self._filesystem_path, only_files=True):
|
for entry in os.scandir(self._filesystem_path):
|
||||||
|
if not entry.is_file():
|
||||||
|
continue
|
||||||
|
href = entry.name
|
||||||
if not is_safe_filesystem_path_component(href):
|
if not is_safe_filesystem_path_component(href):
|
||||||
if not href.startswith(".Radicale"):
|
if not href.startswith(".Radicale"):
|
||||||
logger.debug("Skipping item %r in %r", href, self.path)
|
logger.debug("Skipping item %r in %r", href, self.path)
|
||||||
@ -1357,8 +1336,8 @@ class Collection(BaseCollection):
|
|||||||
cache_folder = os.path.join(self._filesystem_path, ".Radicale.cache",
|
cache_folder = os.path.join(self._filesystem_path, ".Radicale.cache",
|
||||||
"item")
|
"item")
|
||||||
self._clean_cache(cache_folder, (
|
self._clean_cache(cache_folder, (
|
||||||
href for href in scandir(cache_folder) if not
|
e.name for e in os.scandir(cache_folder) if not
|
||||||
os.path.isfile(os.path.join(self._filesystem_path, href))))
|
os.path.isfile(os.path.join(self._filesystem_path, e.name))))
|
||||||
|
|
||||||
def _get_with_metadata(self, href, verify_href=True):
|
def _get_with_metadata(self, href, verify_href=True):
|
||||||
"""Like ``get`` but additonally returns the following metadata:
|
"""Like ``get`` but additonally returns the following metadata:
|
||||||
|
5
setup.py
5
setup.py
@ -74,7 +74,7 @@ setup(
|
|||||||
"md5": "passlib",
|
"md5": "passlib",
|
||||||
"bcrypt": "passlib[bcrypt]"},
|
"bcrypt": "passlib[bcrypt]"},
|
||||||
keywords=["calendar", "addressbook", "CalDAV", "CardDAV"],
|
keywords=["calendar", "addressbook", "CalDAV", "CardDAV"],
|
||||||
python_requires=">=3.3",
|
python_requires=">=3.5.2",
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 5 - Production/Stable",
|
"Development Status :: 5 - Production/Stable",
|
||||||
"Environment :: Console",
|
"Environment :: Console",
|
||||||
@ -84,8 +84,7 @@ setup(
|
|||||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
"License :: OSI Approved :: GNU General Public License (GPL)",
|
||||||
"Operating System :: OS Independent",
|
"Operating System :: OS Independent",
|
||||||
"Programming Language :: Python :: 3",
|
"Programming Language :: Python :: 3",
|
||||||
"Programming Language :: Python :: 3.3",
|
|
||||||
"Programming Language :: Python :: 3.4",
|
|
||||||
"Programming Language :: Python :: 3.5",
|
"Programming Language :: Python :: 3.5",
|
||||||
"Programming Language :: Python :: 3.6",
|
"Programming Language :: Python :: 3.6",
|
||||||
|
"Programming Language :: Python :: 3.7",
|
||||||
"Topic :: Office/Business :: Groupware"])
|
"Topic :: Office/Business :: Groupware"])
|
||||||
|
Loading…
Reference in New Issue
Block a user