Skip to content

Commit

Permalink
API enforces some named keyword args (..., *, ...)
Browse files Browse the repository at this point in the history
  • Loading branch information
mar10 committed Nov 15, 2021
1 parent 92080a4 commit ccffe91
Show file tree
Hide file tree
Showing 22 changed files with 192 additions and 183 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
- Drop support for Python syntax in config files (wsgidav.conf)
- Drop support for Microsoft Web Folders (option `dir_browser.ms_mount`).
- DAVCollection, DAVNonCollection, DAVProvider are ABCs now
- API enforces some named keyword args (`..., *, ...`)
- Move logging options to 'logging' section
- Add uvicorn server support to CLI, drop flup and CherryPy
- DirBrowser supports `?davmount` URLs by default (option `dir_browser.davmount`).
The new option `dir_browser.davmount_links` controls link display (default: false).
- #185 Fix FileLikeQueue for Python 3
- #222 Discrepancy between "getetag" property and ETag header


## 3.1.2 / Unreleased

## 3.1.1 / 2021-07-11
Expand Down
6 changes: 3 additions & 3 deletions docs/source/user_guide_configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,14 +56,14 @@ An alternative file name can be specified like so::

$ wsgidav --config=my_config.yaml

To *prevent* the use of of a local default configuration file, use this option::
To *prevent* the use of a local default configuration file, use this option::

$ wsgidav --no-config

The options described below can be defined for the CLI either

* in `YAML <http://yaml.org/spec/1.2/spec.html>`_ syntax inside a wsgidav.yaml file
* or `JSON <http://www.json.org>`_ syntax inside a wsgidav.json file
* in `YAML <https://yaml.org/spec/>`_ syntax inside a wsgidav.yaml file
* or `JSON <https://www.json.org/>`_ syntax inside a wsgidav.json file

.. note::
The three supported file formats are just different ways for the CLI to
Expand Down
20 changes: 10 additions & 10 deletions tests/test_lock_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,14 +60,14 @@ def _acquire(
"""Wrapper for lm.acquire, that returns None instead of raising DAVError."""
try:
return self.lm.acquire(
url,
lock_type,
lock_scope,
lock_depth,
lock_owner,
timeout,
principal,
token_list,
url=url,
lock_type=lock_type,
lock_scope=lock_scope,
lock_depth=lock_depth,
lock_owner=lock_owner,
timeout=timeout,
principal=principal,
token_list=token_list,
)
except DAVError:
return None
Expand Down Expand Up @@ -213,7 +213,7 @@ def testLock(self):

# Test lookup
tok = lock_dict.get("token")
assert lm.get_lock(tok, "root") == url
assert lm.get_lock(tok, key="root") == url

lock_dict = lm.get_lock(tok)

Expand Down Expand Up @@ -271,7 +271,7 @@ def testTimeout(self):

assert lock_dict is not None
tok = lock_dict.get("token")
assert lm.get_lock(tok, "root") == self.root
assert lm.get_lock(tok, key="root") == self.root

sleep(timeout - 0.5)
lock_dict = lm.get_lock(tok)
Expand Down
4 changes: 2 additions & 2 deletions tests/test_streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def get_etag(self):
def support_etag(self):
return False

def begin_write(self, content_type=None):
def begin_write(self, *, content_type=None):
# print("begin_write: {}".format(self.target_path))
queue = FileLikeQueue(max_size=1)

Expand All @@ -74,7 +74,7 @@ def _consumer():
self.worker.start()
return queue

def end_write(self, with_errors):
def end_write(self, *, with_errors):
print("end_write: {}".format(self.target_path))
self.worker.join()

Expand Down
8 changes: 4 additions & 4 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@
is_child_uri,
is_equal_or_child_uri,
join_uri,
lstripstr,
parse_if_match_header,
pop_path,
removeprefix,
shift_path,
)

Expand Down Expand Up @@ -64,9 +64,9 @@ def testBasics(self):
assert is_equal_or_child_uri("/a/b", "/a/b/c")
assert is_equal_or_child_uri("/a/b", "/a/b/c")

assert lstripstr("/dav/a/b", "/dav") == "/a/b"
assert lstripstr("/dav/a/b", "/DAV") == "/dav/a/b"
assert lstripstr("/dav/a/b", "/DAV", True) == "/a/b"
assert removeprefix("/dav/a/b", "/dav") == "/a/b"
assert removeprefix("/dav/a/b", "/DAV") == "/dav/a/b"
assert removeprefix("/dav/a/b", "/DAV", ignore_case=True) == "/a/b"

assert pop_path("/a/b/c") == ("a", "/b/c")
assert pop_path("/a/b/") == ("a", "/b/")
Expand Down
7 changes: 2 additions & 5 deletions wsgidav/dav_error.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ def as_xml(self):
return error_el

def as_string(self):
return util.to_str(xml_tools.xml_to_bytes(self.as_xml(), True))
return util.to_str(xml_tools.xml_to_bytes(self.as_xml(), pretty=True))


# ========================================================================
Expand All @@ -183,7 +183,7 @@ class DAVError(Exception):
# This would be helpful for debugging.

def __init__(
self, status_code, context_info=None, src_exception=None, err_condition=None
self, status_code, context_info=None, *, src_exception=None, err_condition=None
):
# allow passing of Pre- and Postconditions, see
# http://www.webdav.org/specs/rfc4918.html#precondition.postcondition.xml.elements
Expand All @@ -200,9 +200,6 @@ def __init__(
def __repr__(self):
return "DAVError({})".format(self.get_user_info())

def __str__(self): # Required for 2.4
return self.__repr__()

def get_user_info(self):
"""Return readable string."""
if self.value in ERROR_DESCRIPTIONS:
Expand Down
39 changes: 22 additions & 17 deletions wsgidav/dav_provider.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ def __init__(self, path: str, is_collection: bool, environ: dict):
self.name = util.get_uri_name(self.path)

def __repr__(self):
return "{}({!r})".format(self.__class__.__name__, self.path)
return f"{self.__class__.__name__}({self.path!r})"

# def getContentLanguage(self):
# """Contains the Content-Language header returned by a GET without accept
Expand Down Expand Up @@ -291,7 +291,7 @@ def get_last_modified(self):
"""
return None

def set_last_modified(self, dest_path, time_stamp, dry_run):
def set_last_modified(self, dest_path, time_stamp, *, dry_run):
"""Set last modified time for destPath to timeStamp on epoch-format"""
raise NotImplementedError

Expand Down Expand Up @@ -442,6 +442,7 @@ def get_member_names(self):

def get_descendants(
self,
*,
collections=True,
resources=True,
depth_first=False,
Expand Down Expand Up @@ -480,7 +481,11 @@ def get_descendants(
if child.is_collection and depth == "infinity":
res.extend(
child.get_descendants(
collections, resources, depth_first, depth, add_self=False
collections=collections,
resources=resources,
depth_first=depth_first,
depth=depth,
add_self=False,
)
)
if want and depth_first:
Expand All @@ -491,7 +496,7 @@ def get_descendants(

# --- Properties ---------------------------------------------------------

def get_property_names(self, is_allprop):
def get_property_names(self, *, is_allprop):
"""Return list of supported property names in Clark Notation.
Note that 'allprop', despite its name, which remains for
Expand Down Expand Up @@ -542,7 +547,7 @@ def get_property_names(self, is_allprop):

return propNameList

def get_properties(self, mode, name_list=None):
def get_properties(self, mode, *, name_list=None):
"""Return properties as list of 2-tuples (name, value).
If mode is 'name', then None is returned for the value.
Expand All @@ -568,7 +573,7 @@ def get_properties(self, mode, name_list=None):
# TODO: 'allprop' could have nameList, when <include> option is
# implemented
assert name_list is None
name_list = self.get_property_names(mode == "allprop")
name_list = self.get_property_names(is_allprop=mode == "allprop")
else:
assert name_list is not None

Expand Down Expand Up @@ -726,7 +731,7 @@ def get_property_value(self, name):
# No persistence available, or property not found
raise DAVError(HTTP_NOT_FOUND)

def set_property_value(self, name, value, dry_run=False):
def set_property_value(self, name, value, *, dry_run=False):
"""Set a property value or remove a property.
value == None means 'remove property'.
Expand Down Expand Up @@ -817,7 +822,7 @@ def set_property_value(self, name, value, dry_run=False):

raise DAVError(HTTP_FORBIDDEN)

def remove_all_properties(self, recursive):
def remove_all_properties(self, *, recursive):
"""Remove all associated dead properties."""
if self.provider.prop_manager:
self.provider.prop_manager.remove_properties(
Expand All @@ -841,7 +846,7 @@ def is_locked(self):
return False
return self.provider.lock_manager.is_url_locked(self.get_ref_url())

def remove_all_locks(self, recursive):
def remove_all_locks(self, *, recursive):
if self.provider.lock_manager:
self.provider.lock_manager.remove_all_locks_from_url(self.get_ref_url())

Expand Down Expand Up @@ -895,7 +900,7 @@ def get_content(self):
assert not self.is_collection
raise NotImplementedError

def begin_write(self, content_type=None):
def begin_write(self, *, content_type=None):
"""Open content as a stream for writing.
This method MUST be implemented by all providers that support write
Expand All @@ -904,7 +909,7 @@ def begin_write(self, content_type=None):
assert not self.is_collection
raise DAVError(HTTP_FORBIDDEN)

def end_write(self, with_errors):
def end_write(self, *, with_errors):
"""Called when PUT has finished writing.
This is only a notification. that MAY be handled.
Expand Down Expand Up @@ -989,7 +994,7 @@ def delete(self):
"""
raise NotImplementedError

def handle_copy(self, dest_path, depth_infinity):
def handle_copy(self, dest_path, *, depth_infinity):
"""Handle a COPY request natively.
This method is called by the COPY handler after checking for valid
Expand Down Expand Up @@ -1026,7 +1031,7 @@ def handle_copy(self, dest_path, depth_infinity):
"""
return False

def copy_move_single(self, dest_path, is_move):
def copy_move_single(self, dest_path, *, is_move):
"""Copy or move this resource to destPath (non-recursive).
Preconditions (ensured by caller):
Expand Down Expand Up @@ -1244,15 +1249,15 @@ def support_ranges(self):
"""
return False

def begin_write(self, content_type=None):
def begin_write(self, *, content_type=None):
"""Open content as a stream for writing.
This method MUST be implemented by all providers that support write
access.
"""
raise DAVError(HTTP_FORBIDDEN)

def end_write(self, with_errors):
def end_write(self, *, with_errors):
"""Called when PUT has finished writing.
This is only a notification that MAY be handled.
Expand Down Expand Up @@ -1420,7 +1425,7 @@ def delete(self):
"""
raise DAVError(HTTP_FORBIDDEN)

def copy_move_single(self, dest_path, is_move):
def copy_move_single(self, dest_path, *, is_move):
"""Copy or move this resource to destPath (non-recursive).
This method MUST be implemented if resource allows write access.
Expand Down Expand Up @@ -1522,7 +1527,7 @@ def ref_url_to_path(self, ref_url):
Used to calculate the <path> from a storage key by inverting get_ref_url().
"""
return "/" + unquote(util.lstripstr(ref_url, self.share_path)).lstrip("/")
return "/" + unquote(util.removeprefix(ref_url, self.share_path)).lstrip("/")

@abstractmethod
def get_resource_inst(self, path, environ):
Expand Down
8 changes: 3 additions & 5 deletions wsgidav/error_printer.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,15 @@ def __call__(self, environ, start_response):
)

return
except DAVError as e:
_logger.debug("re-raising {}".format(e))
raise
except DAVError:
raise # Deliberately generated or already converted
except Exception as e:
# Caught a non-DAVError
# Catch all exceptions to return as 500 Internal Error
# traceback.print_exc(10, environ.get("wsgi.errors") or sys.stderr)
_logger.error("{}".format(traceback.format_exc(10)))
raise as_DAVError(e)
except DAVError as e:
_logger.debug("caught {}".format(e))
_logger.debug("Caught {}".format(e))

status = get_http_status_string(e)
# Dump internal errors to console
Expand Down

0 comments on commit ccffe91

Please sign in to comment.