diff options
author | IOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org> | 2015-07-06 22:06:49 +0200 |
---|---|---|
committer | IOhannes m zmölnig (Debian/GNU) <umlaeute@debian.org> | 2015-07-06 22:06:49 +0200 |
commit | 9994c4f88e2290df2b7d2044b397a897927e4893 (patch) | |
tree | 16b4b1cd211c3f8a41f5f2d970f37ceb5b87e491 |
Import python-easywebdav_1.2.0.orig.tar.gz
[dgit import orig python-easywebdav_1.2.0.orig.tar.gz]
-rw-r--r-- | PKG-INFO | 13 | ||||
-rw-r--r-- | easywebdav.egg-info/PKG-INFO | 13 | ||||
-rw-r--r-- | easywebdav.egg-info/SOURCES.txt | 10 | ||||
-rw-r--r-- | easywebdav.egg-info/dependency_links.txt | 1 | ||||
-rw-r--r-- | easywebdav.egg-info/entry_points.txt | 3 | ||||
-rw-r--r-- | easywebdav.egg-info/requires.txt | 1 | ||||
-rw-r--r-- | easywebdav.egg-info/top_level.txt | 1 | ||||
-rw-r--r-- | easywebdav/__init__.py | 5 | ||||
-rw-r--r-- | easywebdav/__version__.py | 1 | ||||
-rw-r--r-- | easywebdav/client.py | 188 | ||||
-rw-r--r-- | setup.cfg | 5 | ||||
-rw-r--r-- | setup.py | 41 |
12 files changed, 282 insertions, 0 deletions
diff --git a/PKG-INFO b/PKG-INFO new file mode 100644 index 0000000..e56df38 --- /dev/null +++ b/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 1.1 +Name: easywebdav +Version: 1.2.0 +Summary: A straight-forward WebDAV client, implemented using Requests +Home-page: http://github.com/amnong/easywebdav +Author: Amnon Grossman +Author-email: emesh1@gmail.com +License: ISC +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3.3 diff --git a/easywebdav.egg-info/PKG-INFO b/easywebdav.egg-info/PKG-INFO new file mode 100644 index 0000000..e56df38 --- /dev/null +++ b/easywebdav.egg-info/PKG-INFO @@ -0,0 +1,13 @@ +Metadata-Version: 1.1 +Name: easywebdav +Version: 1.2.0 +Summary: A straight-forward WebDAV client, implemented using Requests +Home-page: http://github.com/amnong/easywebdav +Author: Amnon Grossman +Author-email: emesh1@gmail.com +License: ISC +Description: UNKNOWN +Platform: UNKNOWN +Classifier: Development Status :: 4 - Beta +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python :: 3.3 diff --git a/easywebdav.egg-info/SOURCES.txt b/easywebdav.egg-info/SOURCES.txt new file mode 100644 index 0000000..420412d --- /dev/null +++ b/easywebdav.egg-info/SOURCES.txt @@ -0,0 +1,10 @@ +setup.py +easywebdav/__init__.py +easywebdav/__version__.py +easywebdav/client.py +easywebdav.egg-info/PKG-INFO +easywebdav.egg-info/SOURCES.txt +easywebdav.egg-info/dependency_links.txt +easywebdav.egg-info/entry_points.txt +easywebdav.egg-info/requires.txt +easywebdav.egg-info/top_level.txt
\ No newline at end of file diff --git a/easywebdav.egg-info/dependency_links.txt b/easywebdav.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/easywebdav.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/easywebdav.egg-info/entry_points.txt b/easywebdav.egg-info/entry_points.txt new file mode 100644 index 0000000..3e1605a --- /dev/null +++ b/easywebdav.egg-info/entry_points.txt @@ -0,0 +1,3 @@ +[console_scripts] + + diff --git a/easywebdav.egg-info/requires.txt b/easywebdav.egg-info/requires.txt new file mode 100644 index 0000000..663bd1f --- /dev/null +++ b/easywebdav.egg-info/requires.txt @@ -0,0 +1 @@ +requests
\ No newline at end of file diff --git a/easywebdav.egg-info/top_level.txt b/easywebdav.egg-info/top_level.txt new file mode 100644 index 0000000..f1a73de --- /dev/null +++ b/easywebdav.egg-info/top_level.txt @@ -0,0 +1 @@ +easywebdav diff --git a/easywebdav/__init__.py b/easywebdav/__init__.py new file mode 100644 index 0000000..4cf9bae --- /dev/null +++ b/easywebdav/__init__.py @@ -0,0 +1,5 @@ +from .client import * + +def connect(*args, **kwargs): + """connect(host, port=0, auth=None, username=None, password=None, protocol='http', path="/")""" + return Client(*args, **kwargs) diff --git a/easywebdav/__version__.py b/easywebdav/__version__.py new file mode 100644 index 0000000..c68196d --- /dev/null +++ b/easywebdav/__version__.py @@ -0,0 +1 @@ +__version__ = "1.2.0" diff --git a/easywebdav/client.py b/easywebdav/client.py new file mode 100644 index 0000000..4003198 --- /dev/null +++ b/easywebdav/client.py @@ -0,0 +1,188 @@ +import requests +import platform +from numbers import Number +import xml.etree.cElementTree as xml +from collections import namedtuple + +py_majversion, py_minversion, py_revversion = platform.python_version_tuple() + +if py_majversion == '2': + from httplib import responses as HTTP_CODES + from urlparse import urlparse +else: + from http.client import responses as HTTP_CODES + from urllib.parse import urlparse + +DOWNLOAD_CHUNK_SIZE_BYTES = 1 * 1024 * 1024 + +class WebdavException(Exception): + pass + +class ConnectionFailed(WebdavException): + pass + + +def codestr(code): + return HTTP_CODES.get(code, 'UNKNOWN') + + +File = namedtuple('File', ['name', 'size', 'mtime', 'ctime', 'contenttype']) + + +def prop(elem, name, default=None): + child = elem.find('.//{DAV:}' + name) + return default if child is None else child.text + + +def elem2file(elem): + return File( + prop(elem, 'href'), + int(prop(elem, 'getcontentlength', 0)), + prop(elem, 'getlastmodified', ''), + prop(elem, 'creationdate', ''), + prop(elem, 'getcontenttype', ''), + ) + + +class OperationFailed(WebdavException): + _OPERATIONS = dict( + HEAD = "get header", + GET = "download", + PUT = "upload", + DELETE = "delete", + MKCOL = "create directory", + PROPFIND = "list directory", + ) + + def __init__(self, method, path, expected_code, actual_code): + self.method = method + self.path = path + self.expected_code = expected_code + self.actual_code = actual_code + operation_name = self._OPERATIONS[method] + self.reason = 'Failed to {operation_name} "{path}"'.format(**locals()) + expected_codes = (expected_code,) if isinstance(expected_code, Number) else expected_code + expected_codes_str = ", ".join('{0} {1}'.format(code, codestr(code)) for code in expected_codes) + actual_code_str = codestr(actual_code) + msg = '''\ +{self.reason}. + Operation : {method} {path} + Expected code : {expected_codes_str} + Actual code : {actual_code} {actual_code_str}'''.format(**locals()) + super(OperationFailed, self).__init__(msg) + +class Client(object): + def __init__(self, host, port=0, auth=None, username=None, password=None, + protocol='http', verify_ssl=True, path=None, cert=None): + if not port: + port = 443 if protocol == 'https' else 80 + self.baseurl = '{0}://{1}:{2}'.format(protocol, host, port) + if path: + self.baseurl = '{0}/{1}'.format(self.baseurl, path) + self.cwd = '/' + self.session = requests.session() + self.session.verify = verify_ssl + self.session.stream = True + + if cert: + self.session.cert = cert + + if auth: + self.session.auth = auth + elif username and password: + self.session.auth = (username, password) + + def _send(self, method, path, expected_code, **kwargs): + url = self._get_url(path) + response = self.session.request(method, url, allow_redirects=False, **kwargs) + if isinstance(expected_code, Number) and response.status_code != expected_code \ + or not isinstance(expected_code, Number) and response.status_code not in expected_code: + raise OperationFailed(method, path, expected_code, response.status_code) + return response + + def _get_url(self, path): + path = str(path).strip() + if path.startswith('/'): + return self.baseurl + path + return "".join((self.baseurl, self.cwd, path)) + + def cd(self, path): + path = path.strip() + if not path: + return + stripped_path = '/'.join(part for part in path.split('/') if part) + '/' + if stripped_path == '/': + self.cwd = stripped_path + elif path.startswith('/'): + self.cwd = '/' + stripped_path + else: + self.cwd += stripped_path + + def mkdir(self, path, safe=False): + expected_codes = 201 if not safe else (201, 301, 405) + self._send('MKCOL', path, expected_codes) + + def mkdirs(self, path): + dirs = [d for d in path.split('/') if d] + if not dirs: + return + if path.startswith('/'): + dirs[0] = '/' + dirs[0] + old_cwd = self.cwd + try: + for dir in dirs: + try: + self.mkdir(dir, safe=True) + except Exception as e: + if e.actual_code == 409: + raise + finally: + self.cd(dir) + finally: + self.cd(old_cwd) + + def rmdir(self, path, safe=False): + path = str(path).rstrip('/') + '/' + expected_codes = 204 if not safe else (204, 404) + self._send('DELETE', path, expected_codes) + + def delete(self, path): + self._send('DELETE', path, 204) + + def upload(self, local_path_or_fileobj, remote_path): + if isinstance(local_path_or_fileobj, basestring): + with open(local_path_or_fileobj, 'rb') as f: + self._upload(f, remote_path) + else: + self._upload(local_path_or_fileobj, remote_path) + + def _upload(self, fileobj, remote_path): + self._send('PUT', remote_path, (200, 201, 204), data=fileobj) + + def download(self, remote_path, local_path_or_fileobj): + response = self._send('GET', remote_path, 200, stream=True) + if isinstance(local_path_or_fileobj, basestring): + with open(local_path_or_fileobj, 'wb') as f: + self._download(f, response) + else: + self._download(local_path_or_fileobj, response) + + def _download(self, fileobj, response): + for chunk in response.iter_content(DOWNLOAD_CHUNK_SIZE_BYTES): + fileobj.write(chunk) + + def ls(self, remote_path='.'): + headers = {'Depth': '1'} + response = self._send('PROPFIND', remote_path, (207, 301), headers=headers) + + # Redirect + if response.status_code == 301: + url = urlparse(response.headers['location']) + return self.ls(url.path) + + tree = xml.fromstring(response.content) + return [elem2file(elem) for elem in tree.findall('{DAV:}response')] + + def exists(self, remote_path): + response = self._send('HEAD', remote_path, (200, 301, 404)) + return True if response.status_code != 404 else False diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..861a9f5 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,5 @@ +[egg_info] +tag_build = +tag_date = 0 +tag_svn_revision = 0 + diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..a2c8a3c --- /dev/null +++ b/setup.py @@ -0,0 +1,41 @@ +import os +import functools +from setuptools import setup, find_packages + +_IN_PACKAGE_DIR = functools.partial(os.path.join, "easywebdav") + +with open(_IN_PACKAGE_DIR("__version__.py")) as version_file: + exec(version_file.read()) + +properties = dict( + name="easywebdav", + classifiers = [ + "Development Status :: 4 - Beta", + "Intended Audience :: Developers", + "Programming Language :: Python :: 3.3", + ], + description="A straight-forward WebDAV client, implemented using Requests", + license="ISC", + author="Amnon Grossman", + author_email="emesh1@gmail.com", + url="http://github.com/amnong/easywebdav", + version=__version__, # noqa + packages=find_packages(exclude=["tests"]), + data_files = [], + install_requires=[ + "requests", + ], + entry_points=dict( + console_scripts=[], + ), + ) + +# Properties for development environments +if "EASYWEBDAV_DEV" in os.environ: + properties["install_requires"].append(( + "nose", + "yanc", + "PyWebDAV", + )) + +setup(**properties)
\ No newline at end of file |