require python>=3.5.2
This commit is contained in:
parent
59d10ef9f7
commit
a5fa35e785
@ -3,10 +3,6 @@ sudo: false
|
||||
|
||||
matrix:
|
||||
include:
|
||||
- os: linux
|
||||
python: 3.3
|
||||
- os: linux
|
||||
python: 3.4
|
||||
- os: linux
|
||||
python: 3.5
|
||||
- os: linux
|
||||
|
@ -73,10 +73,6 @@ class HTTPServer(wsgiref.simple_server.WSGIServer):
|
||||
self.server_close()
|
||||
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):
|
||||
# Set timeout for client
|
||||
_socket, address = super().get_request()
|
||||
@ -249,12 +245,8 @@ def serve(configuration):
|
||||
if configuration.getboolean("server", "ssl") else "")
|
||||
|
||||
# Create a socket pair to notify the select syscall of program shutdown
|
||||
# This is not available in python < 3.5 on Windows
|
||||
if hasattr(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
|
||||
# shutdown
|
||||
@ -265,18 +257,16 @@ def serve(configuration):
|
||||
return
|
||||
logger.info("Stopping Radicale")
|
||||
shutdown_program = True
|
||||
if shutdown_program_socket_in:
|
||||
shutdown_program_socket_in.sendall(b"goodbye")
|
||||
shutdown_program_socket_in.sendall(b" ")
|
||||
signal.signal(signal.SIGTERM, shutdown)
|
||||
signal.signal(signal.SIGINT, shutdown)
|
||||
|
||||
# Main loop: wait for requests on any of the servers or program shutdown
|
||||
sockets = list(servers.keys())
|
||||
if shutdown_program_socket_out:
|
||||
# Use socket pair to get notified of program shutdown
|
||||
sockets.append(shutdown_program_socket_out)
|
||||
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.)
|
||||
select_timeout = 1.0
|
||||
logger.info("Radicale server ready")
|
||||
|
@ -34,7 +34,6 @@ import pickle
|
||||
import posixpath
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
from contextlib import contextmanager
|
||||
@ -47,12 +46,9 @@ from tempfile import NamedTemporaryFile, TemporaryDirectory
|
||||
|
||||
import vobject
|
||||
|
||||
from radicale import xmlutils
|
||||
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":
|
||||
import ctypes
|
||||
import ctypes.wintypes
|
||||
@ -97,10 +93,6 @@ INTERNAL_TYPES = ("multifilesystem",)
|
||||
|
||||
def load(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")
|
||||
if storage_type == "multifilesystem":
|
||||
collection_class = Collection
|
||||
@ -241,27 +233,6 @@ def find_available_name(exists_fn, suffix=""):
|
||||
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):
|
||||
"""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
|
||||
# or short names on Windows file systems)
|
||||
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)
|
||||
return safe_path
|
||||
|
||||
@ -899,7 +871,10 @@ class Collection(BaseCollection):
|
||||
with child_context_manager(sane_path, 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 href.startswith(".Radicale"):
|
||||
logger.debug("Skipping collection %r in %r",
|
||||
@ -1174,7 +1149,8 @@ class Collection(BaseCollection):
|
||||
history_folder = os.path.join(self._filesystem_path,
|
||||
".Radicale.cache", "history")
|
||||
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):
|
||||
continue
|
||||
if os.path.isfile(os.path.join(self._filesystem_path, href)):
|
||||
@ -1285,7 +1261,10 @@ class Collection(BaseCollection):
|
||||
return token, changes
|
||||
|
||||
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 href.startswith(".Radicale"):
|
||||
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",
|
||||
"item")
|
||||
self._clean_cache(cache_folder, (
|
||||
href for href in scandir(cache_folder) if not
|
||||
os.path.isfile(os.path.join(self._filesystem_path, href))))
|
||||
e.name for e in os.scandir(cache_folder) if not
|
||||
os.path.isfile(os.path.join(self._filesystem_path, e.name))))
|
||||
|
||||
def _get_with_metadata(self, href, verify_href=True):
|
||||
"""Like ``get`` but additonally returns the following metadata:
|
||||
|
5
setup.py
5
setup.py
@ -74,7 +74,7 @@ setup(
|
||||
"md5": "passlib",
|
||||
"bcrypt": "passlib[bcrypt]"},
|
||||
keywords=["calendar", "addressbook", "CalDAV", "CardDAV"],
|
||||
python_requires=">=3.3",
|
||||
python_requires=">=3.5.2",
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: Console",
|
||||
@ -84,8 +84,7 @@ setup(
|
||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Programming Language :: Python :: 3.3",
|
||||
"Programming Language :: Python :: 3.4",
|
||||
"Programming Language :: Python :: 3.5",
|
||||
"Programming Language :: Python :: 3.6",
|
||||
"Programming Language :: Python :: 3.7",
|
||||
"Topic :: Office/Business :: Groupware"])
|
||||
|
Loading…
Reference in New Issue
Block a user