Compare commits

..

No commits in common. "develop" and "release-0.2" have entirely different histories.

30 changed files with 708 additions and 1942 deletions

9
.gitignore vendored
View file

@ -1,11 +1,2 @@
*.pyc *.pyc
/**/*.pyc /**/*.pyc
/dist
/*.egg-info
/.eggs/
.project
.pydevproject
/.settings/
venv/
.idea/
build

View file

@ -1,32 +0,0 @@
language: python
dist: xenial
addons:
sonarcloud:
organization: "ezhov-evgeny"
token: f0f714f3bea6bd103e3eb82724ef3bb0d3b54d1d
services:
- docker
python:
- "3.5"
- "3.6"
- "3.7"
- "3.8"
before_install:
- docker pull bytemark/webdav
- docker run -d --name webdav -e AUTH_TYPE=Basic -e USERNAME=alice -e PASSWORD=secret1234 -p 8585:80 bytemark/webdav
- docker ps -a
install:
- python setup.py develop
- pip install coverage
script:
- coverage run setup.py test
- coverage xml
- |
if [[ $TRAVIS_PYTHON_VERSION == "3.8" ]]; then
sonar-scanner
fi

View file

@ -1,4 +1,4 @@
COPYRIGHT AND PERMISSION NOTICE python setup.py register -r pypitestCOPYRIGHT AND PERMISSION NOTICE
Copyright (c) 2016, The WDC Project, and many Copyright (c) 2016, The WDC Project, and many
contributors, see the THANKS file. contributors, see the THANKS file.

378
README.md
View file

@ -1,378 +0,0 @@
webdavclient3
=========
[![Build Status](https://travis-ci.com/ezhov-evgeny/webdav-client-python-3.svg?branch=develop)](https://travis-ci.com/ezhov-evgeny/webdav-client-python-3)
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=ezhov-evgeny_webdav-client-python-3&metric=alert_status)](https://sonarcloud.io/dashboard?id=ezhov-evgeny_webdav-client-python-3)
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=ezhov-evgeny_webdav-client-python-3&metric=coverage)](https://sonarcloud.io/dashboard?id=ezhov-evgeny_webdav-client-python-3)
[![PyPI](https://img.shields.io/pypi/v/webdavclient3)](https://pypi.org/project/webdavclient3/) ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/webdavclient3)
Package webdavclient3 based on https://github.com/designerror/webdav-client-python but uses `requests` instead of `PyCURL`.
It provides easy way to work with WebDAV-servers.
Installation
------------
```bash
$ pip install webdavclient3
```
Sample Usage
------------
```python
from webdav3.client import Client
options = {
'webdav_hostname': "https://webdav.server.ru",
'webdav_login': "login",
'webdav_password': "password"
}
client = Client(options)
client.verify = False # To not check SSL certificates (Default = True)
client.session.proxies(...) # To set proxy directly into the session (Optional)
client.session.auth(...) # To set proxy auth directly into the session (Optional)
client.execute_request("mkdir", 'directory_name')
```
Webdav API
==========
Webdav API is a set of webdav actions of work with cloud storage. This set includes the following actions:
`check`, `free`, `info`, `list`, `mkdir`, `clean`, `copy`, `move`, `download`, `upload`, `publish` and `unpublish`.
**Configuring the client**
Required keys for configuring client connection with WevDAV-server are `webdav_hostname` and `webdav_login`, `webdav_password`.
```python
from webdav3.client import Client
options = {
'webdav_hostname': "https://webdav.server.ru",
'webdav_login': "login",
'webdav_password': "password"
}
client = Client(options)
```
If your server does not support `HEAD` method or there are other reasons to override default WebDAV methods for actions use a dictionary option `webdav_override_methods`.
The key should be in the following list: `check`, `free`, `info`, `list`, `mkdir`, `clean`, `copy`, `move`, `download`, `upload`,
`publish` and `unpublish`. The value should a string name of WebDAV method, for example `GET`.
```python
from webdav3.client import Client
options = {
'webdav_hostname': "https://webdav.server.ru",
'webdav_login': "login",
'webdav_password': "password",
'webdav_override_methods': {
'check': 'GET'
}
}
client = Client(options)
```
When a proxy server you need to specify settings to connect through it.
```python
from webdav3.client import Client
options = {
'webdav_hostname': "https://webdav.server.ru",
'webdav_login': "w_login",
'webdav_password': "w_password",
'proxy_hostname': "http://127.0.0.1:8080",
'proxy_login': "p_login",
'proxy_password': "p_password"
}
client = Client(options)
```
If you want to use the certificate path to certificate and private key is defined as follows:
```python
from webdav3.client import Client
options = {
'webdav_hostname': "https://webdav.server.ru",
'webdav_login': "w_login",
'webdav_password': "w_password",
'cert_path': "/etc/ssl/certs/certificate.crt",
'key_path': "/etc/ssl/private/certificate.key"
}
client = Client(options)
```
Or you want to limit the speed or turn on verbose mode:
```python
options = {
...
'recv_speed' : 3000000,
'send_speed' : 3000000,
'verbose' : True
}
client = Client(options)
```
recv_speed: rate limit data download speed in Bytes per second. Defaults to unlimited speed.
send_speed: rate limit data upload speed in Bytes per second. Defaults to unlimited speed.
verbose: set verbose mode on/off. By default verbose mode is off.
Also if your server does not support `check` it is possible to disable it:
```python
options = {
...
'disable_check': True
}
client = Client(options)
```
By default checking of remote resources is enabled.
**Synchronous methods**
```python
# Checking existence of the resource
client.check("dir1/file1")
client.check("dir1")
```
```python
# Get information about the resource
client.info("dir1/file1")
client.info("dir1/")
```
```python
# Check free space
free_size = client.free()
```
```python
# Get a list of resources
files1 = client.list()
files2 = client.list("dir1")
```
```python
# Create directory
client.mkdir("dir1/dir2")
```
```python
# Delete resource
client.clean("dir1/dir2")
```
```python
# Copy resource
client.copy(remote_path_from="dir1/file1", remote_path_to="dir2/file1")
client.copy(remote_path_from="dir2", remote_path_to="dir3")
```
```python
# Move resource
client.move(remote_path_from="dir1/file1", remote_path_to="dir2/file1")
client.move(remote_path_from="dir2", remote_path_to="dir3")
```
```python
# Move resource
client.download_sync(remote_path="dir1/file1", local_path="~/Downloads/file1")
client.download_sync(remote_path="dir1/dir2/", local_path="~/Downloads/dir2/")
```
```python
# Unload resource
client.upload_sync(remote_path="dir1/file1", local_path="~/Documents/file1")
client.upload_sync(remote_path="dir1/dir2/", local_path="~/Documents/dir2/")
```
```python
# Publish the resource
link = client.publish("dir1/file1")
link = client.publish("dir2")
```
```python
# Unpublish resource
client.unpublish("dir1/file1")
client.unpublish("dir2")
```
```python
# Exception handling
from webdav3.client import WebDavException
try:
...
except WebDavException as exception:
...
```
```python
# Get the missing files
client.pull(remote_directory='dir1', local_directory='~/Documents/dir1')
```
```python
# Send missing files
client.push(remote_directory='dir1', local_directory='~/Documents/dir1')
```
**Asynchronous methods**
```python
# Load resource
kwargs = {
'remote_path': "dir1/file1",
'local_path': "~/Downloads/file1",
'callback': callback
}
client.download_async(**kwargs)
kwargs = {
'remote_path': "dir1/dir2/",
'local_path': "~/Downloads/dir2/",
'callback': callback
}
client.download_async(**kwargs)
```
```python
# Unload resource
kwargs = {
'remote_path': "dir1/file1",
'local_path': "~/Downloads/file1",
'callback': callback
}
client.upload_async(**kwargs)
kwargs = {
'remote_path': "dir1/dir2/",
'local_path': "~/Downloads/dir2/",
'callback': callback
}
client.upload_async(**kwargs)
```
Resource API
============
Resource API using the concept of OOP that enables cloud-level resources.
```python
# Get a resource
res1 = client.resource("dir1/file1")
```
```python
# Work with the resource
res1.rename("file2")
res1.move("dir1/file2")
res1.copy("dir2/file1")
info = res1.info()
res1.read_from(buffer)
res1.read(local_path="~/Documents/file1")
res1.read_async(local_path="~/Documents/file1", callback)
res1.write_to(buffer)
res1.write(local_path="~/Downloads/file1")
res1.write_async(local_path="~/Downloads/file1", callback)
```
Release Notes
-------------
**Version 3.14.1**
* Fixed issue during coping and moving files with cyrillic names
* Support OAuth2 bearer tokens by https://github.com/danielloader
**Version 3.14**
* Override methods for customizing communication with WebDAV servers
* Support multiple clients simultaneously
* Sync modified files during pull and push by https://github.com/mont5piques
**Version 0.14**
* Fixed an issue with checking resources on Yandex WebDAV server
**Version 0.13 27.11.2019**
* Main version of Python is updated up to 3.7
* Switch to use python sessions rather than requests by https://github.com/delrey1
* Stripping suburl from paths in extract_response_for_path by https://github.com/Skeen
* Added Docker Web DAV for CI
* Changed HEAD to GET method for 'check' request due of not all servers support HEAD by request of https://github.com/danieleTrimarchi
* Removed a costy is_dir-check on obvious directories to speed up a pull by https://github.com/jolly-jump
* Added an option to disable check in case WebDAV server is not support it by request of https://github.com/dzhuang
**Version 0.12 - 21.06.2019**
* Added depth argument in copy method in client.py by https://github.com/JesperHakansson
* Added verify attribute to execute_request method by https://github.com/JesperHakansson
**Version 0.11 30.03.2019**
* Fixed MemoryError if a large file is downloaded with a 32 bit python by https://github.com/bboehmke
* Fixed argcomplete is required to run wdc but was not included in the requirements by https://github.com/evanhorn
* Fixed wdc tries to import webdav instead of webdav3 by https://github.com/evanhorn
**Version 0.10 31.01.2019**
* AssertEquals deprecation warnings by https://github.com/StefanZi
* Problems with byte/UTF strings and xml library by https://github.com/StefanZi
* Add some Eclipse specific files to gitignore by https://github.com/StefanZi
* Remove filesize limit by https://github.com/StefanZi
**Version 0.9 10.05.2018**
* Client.mkdir now accepts 201 HTTP-code by https://github.com/a1ezzz
* Tests are updated
* Added Travis-CI
**Version 0.8 07.05.2018**
* Fixed issue in extract_response_for_path when a link in "href" attribute is an absolute link by https://github.com/a1ezzz
**Version 0.7 16.03.2018**
* Fixed issue with wrong argument for resource creation by https://github.com/janLo
**Version 0.6 21.02.2018**
* Fixed issue with in extracting response for path by https://github.com/mightydok
**Version 0.5 03.12.2017**
* Added method for setting of WebDAV resource property values in batch
**Version 0.4 - 27.11.2017**
* Refactoring of WebDAV client and making it works in following methods:
- Checking is remote resource directory
- Fixed problem when connection lost during request executing and nothing was happened, now it raises an exception
**Version 0.3 - 18.10.2017**
* Refactoring of WebDAV client and making it works in following methods:
- Getting of WebDAV resource property value
- Setting of WebDAV resource property value
- Coping of resource on WebDAV server
- Moving of resource on WebDAV server
- Deleting of resource on WebDAV server
- Getting of information about WebDAV resource
**Version 0.2 - 11.09.2017**
* Refactoring of WebDAV client and making it works in following methods:
- Constructor with connecting to WebDAV
- Getting a list of resources on WebDAV
- Getting an information about free space on WebDAV
- Checking of existence of resource on WebDAV
- Making a directory on WebDAV
- Downloading of files and directories from WebDAV
- Asynchronously downloading of files and directories from WebDAV
- Uploading of files and directories to WebDAV
- Asynchronously uploading of files and directories to WebDAV

5
README.rst Normal file
View file

@ -0,0 +1,5 @@
webdavclient3
============
Based on https://github.com/designerror/webdav-client-python
But uses `requests` instead of `PyCURL`

View file

@ -1,8 +0,0 @@
# Clean old dists
rm -r dist
# Create new packege
python setup.py sdist
# Upload to pypi
twine upload dist/*

View file

@ -6,7 +6,7 @@ from setuptools import setup, find_packages
from setuptools.command.install import install as InstallCommand from setuptools.command.install import install as InstallCommand
from setuptools.command.test import test as TestCommand from setuptools.command.test import test as TestCommand
version = "3.14.1" version = "0.2"
requirements = "libxml2-dev libxslt-dev python-dev" requirements = "libxml2-dev libxslt-dev python-dev"
@ -42,28 +42,20 @@ class Test(TestCommand):
errno = pytest.main(self.pytest_args) errno = pytest.main(self.pytest_args)
sys.exit(errno) sys.exit(errno)
try:
long_description = open('README.md', encoding="utf-8").read()
except TypeError:
long_description = open('README.md').read()
setup( setup(
name='webdavclient3', name='webdavclient3',
version=version, version=version,
packages=find_packages(), packages=find_packages(),
requires=['python (>= 3.3.0)'], requires=['python (>= 2.7.6)'],
install_requires=['requests', 'lxml', 'python-dateutil'], install_requires=['requests', 'lxml'],
scripts=['wdc'], scripts=['wdc'],
test_suite='tests', tests_require=['pytest', 'pyhamcrest', 'junit-xml', 'pytest-allure-adaptor'],
tests_require=['pytest'],
cmdclass={'install': Install, 'test': Test}, cmdclass={'install': Install, 'test': Test},
description='WebDAV client, based on original package https://github.com/designerror/webdav-client-python but ' description='WebDAV client, based on original package https://github.com/designerror/webdav-client-python but uses requests instead of PyCURL',
'uses requests instead of PyCURL', long_description=open('README.rst').read(),
long_description=long_description,
long_description_content_type='text/markdown',
author='Evgeny Ezhov', author='Evgeny Ezhov',
author_email='ezhov.evgeny@gmail.com', author_email='ezhov.evgeny@gmail.com',
url='https://github.com/ezhov-evgeny/webdav-client-python-3', url='https://github.com/ezhov-evgeny/webdav-client-python-2',
license='MIT License', license='MIT License',
keywords='webdav, client, python, module, library, packet, Yandex.Disk, Dropbox, Google Disk, Box, 4shared', keywords='webdav, client, python, module, library, packet, Yandex.Disk, Dropbox, Google Disk, Box, 4shared',
classifiers=[ classifiers=[
@ -73,10 +65,13 @@ setup(
'Operating System :: MacOS', 'Operating System :: MacOS',
'Operating System :: Microsoft', 'Operating System :: Microsoft',
'Operating System :: Unix', 'Operating System :: Unix',
'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.0',
'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.1',
'Programming Language :: Python :: 3.2',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Topic :: Internet', 'Topic :: Internet',
'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Libraries :: Python Modules',
], ],

View file

@ -1,11 +0,0 @@
sonar.projectKey=ezhov-evgeny_webdav-client-python-3
sonar.organization=ezhov-evgeny
# This is the name and version displayed in the SonarCloud UI.
sonar.projectName=webdav-client-python-3
sonar.projectVersion=0.13
# Path is relative to the sonar-project.properties file. Replace "\" by "/" on Windows.
sonar.sources=webdav3
sonar.tests=tests
sonar.python.coverage.reportPaths=coverage.xml
# Encoding of the source code. Default is default system encoding
sonar.sourceEncoding=UTF-8

View file

View file

@ -1,83 +0,0 @@
import os
import shutil
import unittest
from os import path
from webdav3.client import Client
class BaseClientTestCase(unittest.TestCase):
remote_path_file = 'test_dir/test.txt'
remote_path_file2 = 'test_dir2/test.txt'
remote_inner_path_file = 'test_dir/inner/test.txt'
remote_path_dir = 'test_dir'
remote_path_dir2 = 'test_dir2'
remote_inner_path_dir = 'test_dir/inner'
inner_dir_name = 'inner'
local_base_dir = 'tests/'
local_file = 'test.txt'
local_file_path = local_base_dir + 'test.txt'
local_path_dir = local_base_dir + 'res/test_dir'
options = {
'webdav_hostname': 'http://localhost:8585',
'webdav_login': 'alice',
'webdav_password': 'secret1234',
'webdav_override_methods': {
'check': 'GET'
}
}
# options = {
# 'webdav_hostname': 'https://webdav.yandex.ru',
# 'webdav_login': 'webdavclient.test2',
# 'webdav_password': 'Qwerty123!'
# }
def setUp(self):
self.client = Client(self.options)
self.clean_local_dir(self.local_path_dir)
def tearDown(self):
self.clean_local_dir(self.local_path_dir)
self.clean_remote_dir(self.remote_path_dir)
self.clean_remote_dir(self.remote_path_dir2)
def clean_remote_dir(self, remote_path_dir):
if self.client.check(remote_path=remote_path_dir):
self.client.clean(remote_path=remote_path_dir)
@staticmethod
def clean_local_dir(local_path_dir):
if path.exists(path=local_path_dir):
shutil.rmtree(path=local_path_dir)
def _prepare_for_downloading(self, inner_dir=False, base_path=''):
if base_path:
self._create_remote_dir_if_needed(base_path)
self._prepare_dir_for_downloading(base_path + self.remote_path_dir, base_path + self.remote_path_file, self.local_file_path)
if not path.exists(self.local_path_dir):
os.makedirs(self.local_path_dir)
if inner_dir:
self._prepare_dir_for_downloading(base_path + self.remote_inner_path_dir, base_path + self.remote_inner_path_file, self.local_file_path)
def _prepare_dir_for_downloading(self, remote_path_dir, remote_path_file, local_file_path):
self._create_remote_dir_if_needed(remote_path_dir)
if not self.client.check(remote_path=remote_path_file):
self.client.upload_file(remote_path=remote_path_file, local_path=local_file_path)
def _create_remote_dir_if_needed(self, remote_dir):
if not self.client.check(remote_path=remote_dir):
self.client.mkdir(remote_path=remote_dir)
def _prepare_for_uploading(self):
if not self.client.check(remote_path=self.remote_path_dir):
self.client.mkdir(remote_path=self.remote_path_dir)
if not path.exists(path=self.local_path_dir):
os.makedirs(self.local_path_dir)
if not path.exists(path=self.local_path_dir + os.sep + self.local_file):
shutil.copy(src=self.local_file_path, dst=self.local_path_dir + os.sep + self.local_file)
if __name__ == '__main__':
unittest.main()

32
tests/conftest.py Normal file
View file

@ -0,0 +1,32 @@
__author__ = 'designerror'
from hamcrest.core.base_matcher import BaseMatcher
from hamcrest.core.helpers.hasmethod import hasmethod
class Valid(BaseMatcher):
def _matches(self, item):
return False if not hasmethod(item, 'valid') else item.valid()
class NotValid(BaseMatcher):
def _matches(self, item):
return False if not hasmethod(item, 'valid') else not item.valid()
class Success(BaseMatcher):
def _matches(self, item):
return item
class NotSuccess(BaseMatcher):
def _matches(self, item):
return not item
def valid():
return Valid()
def not_valid():
return NotValid()
def success():
return Success()
def not_success():
return NotSuccess()

View file

@ -1,136 +0,0 @@
<?xml version='1.0' encoding='UTF-8'?>
<d:multistatus xmlns:d="DAV:">
<d:response>
<d:href>/</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:creationdate>2012-04-04T20:00:00Z</d:creationdate>
<d:displayname>disk</d:displayname>
<d:getlastmodified>Wed, 04 Apr 2012 20:00:00 GMT</d:getlastmodified>
<d:resourcetype>
<d:collection/>
</d:resourcetype>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/test_dir/</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:creationdate>2018-05-10T07:31:13Z</d:creationdate>
<d:displayname>test_dir</d:displayname>
<d:getlastmodified>Thu, 10 May 2018 07:31:13 GMT</d:getlastmodified>
<d:resourcetype>
<d:collection/>
</d:resourcetype>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%93%D0%BE%D1%80%D1%8B.jpg</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>1392851f0668017168ee4b5a59d66e7b</d:getetag>
<d:creationdate>2018-05-09T14:44:28Z</d:creationdate>
<d:displayname>Горы.jpg</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:28 GMT</d:getlastmodified>
<d:getcontenttype>image/jpeg</d:getcontenttype>
<d:getcontentlength>1762478</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%97%D0%B8%D0%BC%D0%B0.jpg</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>a64146fee5e15b3b94c204e544426d43</d:getetag>
<d:creationdate>2018-05-09T14:44:28Z</d:creationdate>
<d:displayname>Зима.jpg</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:28 GMT</d:getlastmodified>
<d:getcontenttype>image/jpeg</d:getcontenttype>
<d:getcontentlength>1394575</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%9C%D0%B8%D1%88%D0%BA%D0%B8.jpg</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>569a1c98696050439b5b2a1ecfa52d19</d:getetag>
<d:creationdate>2018-05-09T14:44:27Z</d:creationdate>
<d:displayname>Мишки.jpg</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:27 GMT</d:getlastmodified>
<d:getcontenttype>image/jpeg</d:getcontenttype>
<d:getcontentlength>1555830</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%9C%D0%BE%D1%80%D0%B5.jpg</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>ab903d9cab031eca2a8f12f37bbc9d37</d:getetag>
<d:creationdate>2018-05-09T14:44:27Z</d:creationdate>
<d:displayname>Море.jpg</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:27 GMT</d:getlastmodified>
<d:getcontenttype>image/jpeg</d:getcontenttype>
<d:getcontentlength>1080301</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%9C%D0%BE%D1%81%D0%BA%D0%B2%D0%B0.jpg</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>d27d72a3059ad5ebed7a5470459d2670</d:getetag>
<d:creationdate>2018-05-09T14:44:27Z</d:creationdate>
<d:displayname>Москва.jpg</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:27 GMT</d:getlastmodified>
<d:getcontenttype>image/jpeg</d:getcontenttype>
<d:getcontentlength>1454228</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3.jpg</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>f1abe3b27b410128623fd1ca00a45c29</d:getetag>
<d:creationdate>2018-05-09T14:44:27Z</d:creationdate>
<d:displayname>Санкт-Петербург.jpg</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:27 GMT</d:getlastmodified>
<d:getcontenttype>image/jpeg</d:getcontenttype>
<d:getcontentlength>2573704</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
<d:response>
<d:href>/%D0%A5%D0%BB%D0%B5%D0%B1%D0%BD%D1%8B%D0%B5%20%D0%BA%D1%80%D0%BE%D1%88%D0%BA%D0%B8.mp4</d:href>
<d:propstat>
<d:status>HTTP/1.1 200 OK</d:status>
<d:prop>
<d:getetag>ea977f513074d5524bee3638798183b9</d:getetag>
<d:creationdate>2018-05-09T14:44:28Z</d:creationdate>
<d:displayname>Хлебные крошки.mp4</d:displayname>
<d:getlastmodified>Wed, 09 May 2018 14:44:28 GMT</d:getlastmodified>
<d:getcontenttype>video/mp4</d:getcontenttype>
<d:getcontentlength>31000079</d:getcontentlength>
<d:resourcetype/>
</d:prop>
</d:propstat>
</d:response>
</d:multistatus>

140
tests/test_client.py Normal file
View file

@ -0,0 +1,140 @@
import os.path
import shutil
from io import BytesIO, StringIO
from os import path
from unittest import TestCase
from webdav3.client import Client
class ClientTestCase(TestCase):
remote_path_file = 'test_dir/test.txt'
remote_path_dir = 'test_dir'
local_path_file = 'test.txt'
local_path_dir = u'res/test_dir'
def setUp(self):
options = {
'webdav_hostname': 'https://webdav.yandex.ru',
'webdav_login': 'webdavclient.test',
'webdav_password': 'Qwerty123!'
}
self.client = Client(options)
if path.exists(path=self.local_path_dir):
shutil.rmtree(path=self.local_path_dir)
def test_list(self):
file_list = self.client.list()
self.assertIsNotNone(file_list, 'List of files should not be None')
self.assertGreater(file_list.__len__(), 0, 'Expected that amount of files more then 0')
def test_free(self):
self.assertGreater(self.client.free(), 0, 'Expected that free space on WebDAV server is more then 0 bytes')
def test_check(self):
self.assertTrue(self.client.check(), 'Expected that root directory is exist')
def test_mkdir(self):
if self.client.check(remote_path=self.remote_path_dir):
self.client.clean(remote_path=self.remote_path_dir)
self.client.mkdir(remote_path=self.remote_path_dir)
self.assertTrue(self.client.check(remote_path=self.remote_path_dir), 'Expected the directory is created.')
def test_download_to(self):
buff = BytesIO()
self.client.download_to(buff=buff, remote_path=self.remote_path_file)
self.assertEquals(buff.getvalue(), 'test content for testing of webdav client')
def test_download(self):
self._prepare_for_downloading()
self.client.download(local_path=self.local_path_dir, remote_path=self.remote_path_dir)
self.assertTrue(path.exists(self.local_path_dir), 'Expected the directory is downloaded.')
self.assertTrue(path.isdir(self.local_path_dir), 'Expected this is a directory.')
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected this is a file')
def test_download_sync(self):
self._prepare_for_downloading()
os.mkdir(self.local_path_dir)
def callback():
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected this is a file')
self.client.download_sync(local_path=self.local_path_dir + os.path.sep + self.local_path_file,
remote_path=self.remote_path_file, callback=callback)
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected the file has already been downloaded')
def test_download_async(self):
self._prepare_for_downloading()
os.mkdir(self.local_path_dir)
def callback():
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected this is a file')
self.client.download_async(local_path=self.local_path_dir + os.path.sep + self.local_path_file,
remote_path=self.remote_path_file, callback=callback)
self.assertFalse(path.exists(self.local_path_dir + os.path.sep + self.local_path_file),
'Expected the file has not been downloaded yet')
def test_upload_from(self):
self._prepare_for_uploading()
buff = StringIO(u'test content for testing of webdav client')
self.client.upload_from(buff=buff, remote_path=self.remote_path_file)
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
self.test_download_to()
def test_upload(self):
self._prepare_for_uploading()
self.client.upload(remote_path=self.remote_path_file, local_path=self.local_path_dir)
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
def test_upload_file(self):
self._prepare_for_uploading()
self.client.upload_file(remote_path=self.remote_path_file, local_path=self.local_path_file)
self.assertTrue(self.client.check(remote_path=self.remote_path_file), 'Expected the file is uploaded.')
def test_upload_sync(self):
self._prepare_for_uploading()
def callback():
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
self.client.upload(remote_path=self.remote_path_file, local_path=self.local_path_dir)
def test_upload_async(self):
self._prepare_for_uploading()
def callback():
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
self.client.upload(remote_path=self.remote_path_file, local_path=self.local_path_dir)
def _prepare_for_downloading(self):
if not self.client.check(remote_path=self.remote_path_dir):
self.client.mkdir(remote_path=self.remote_path_dir)
if not self.client.check(remote_path=self.remote_path_file):
self.client.upload_file(remote_path=self.remote_path_file, local_path=self.local_path_file)
def _prepare_for_uploading(self):
if self.client.check(remote_path=self.remote_path_file):
self.client.clean(remote_path=self.remote_path_file)
if not self.client.check(remote_path=self.remote_path_dir):
self.client.mkdir(remote_path=self.remote_path_dir)
if not path.exists(path=self.local_path_dir):
os.mkdir(self.local_path_dir)
if not path.exists(path=self.local_path_dir + os.sep + self.local_path_file):
shutil.copy(src=self.local_path_file, dst=self.local_path_dir + os.sep + self.local_path_file)

View file

@ -1,283 +0,0 @@
import os.path
import shutil
import unittest
from io import BytesIO, StringIO
from os import path
from time import sleep
from tests.base_client_it import BaseClientTestCase
from webdav3.exceptions import MethodNotSupported, OptionNotValid, RemoteResourceNotFound
class ClientTestCase(BaseClientTestCase):
pulled_file = BaseClientTestCase.local_path_dir + os.sep + BaseClientTestCase.local_file
def test_list(self):
self._prepare_for_downloading()
file_list = self.client.list()
self.assertIsNotNone(file_list, 'List of files should not be None')
self.assertGreater(file_list.__len__(), 0, 'Expected that amount of files more then 0')
def test_free(self):
if 'localhost' in self.options['webdav_hostname']:
with self.assertRaises(MethodNotSupported):
self.client.free()
else:
self.assertGreater(self.client.free(), 0, 'Expected that free space on WebDAV server is more then 0 bytes')
def test_check(self):
self.assertTrue(self.client.check(), 'Expected that root directory is exist')
def test_mkdir(self):
if self.client.check(remote_path=self.remote_path_dir):
self.client.clean(remote_path=self.remote_path_dir)
self.client.mkdir(remote_path=self.remote_path_dir)
self.assertTrue(self.client.check(remote_path=self.remote_path_dir), 'Expected the directory is created.')
def test_download_from(self):
self._prepare_for_downloading()
buff = BytesIO()
self.client.download_from(buff=buff, remote_path=self.remote_path_file)
self.assertEqual(buff.getvalue(), b'test content for testing of webdav client')
def test_download_from_dir(self):
self._prepare_for_downloading()
buff = BytesIO()
with self.assertRaises(OptionNotValid):
self.client.download_from(buff=buff, remote_path=self.remote_path_dir)
def test_download_from_wrong_file(self):
self._prepare_for_downloading()
buff = BytesIO()
with self.assertRaises(RemoteResourceNotFound):
self.client.download_from(buff=buff, remote_path='wrong')
def test_download_directory_wrong(self):
self._prepare_for_downloading()
with self.assertRaises(RemoteResourceNotFound):
self.client.download_directory(remote_path=self.remote_path_file, local_path=self.local_path_dir)
def test_download(self):
self._prepare_for_downloading()
self.client.download(local_path=self.local_path_dir, remote_path=self.remote_path_dir)
self.assertTrue(path.exists(self.local_path_dir), 'Expected the directory is downloaded.')
self.assertTrue(path.isdir(self.local_path_dir), 'Expected this is a directory.')
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_file),
'Expected this is a file')
def test_download_sync(self):
self._prepare_for_downloading()
def callback():
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_file),
'Expected this is a file')
self.client.download_sync(local_path=self.local_path_dir + os.path.sep + self.local_file,
remote_path=self.remote_path_file, callback=callback)
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_file),
'Expected the file has already been downloaded')
def test_download_async(self):
self._prepare_for_downloading()
def callback():
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_file),
'Expected this is a file')
self.client.download_async(local_path=self.local_path_dir + os.path.sep + self.local_file,
remote_path=self.remote_path_file, callback=callback)
self.assertFalse(path.exists(self.local_path_dir + os.path.sep + self.local_file),
'Expected the file has not been downloaded yet')
# It needs for ending download before environment will be cleaned in tearDown
sleep(0.4)
def test_upload_from(self):
self._prepare_for_uploading()
buff = StringIO()
buff.write(u'test content for testing of webdav client')
self.client.upload_to(buff=buff, remote_path=self.remote_path_file)
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
def test_upload(self):
self._prepare_for_uploading()
self.client.upload(remote_path=self.remote_path_file, local_path=self.local_path_dir)
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
def test_upload_file(self):
self._prepare_for_uploading()
self.client.upload_file(remote_path=self.remote_path_file, local_path=self.local_file_path)
self.assertTrue(self.client.check(remote_path=self.remote_path_file), 'Expected the file is uploaded.')
def test_upload_sync(self):
self._prepare_for_uploading()
def callback():
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
self.client.upload(remote_path=self.remote_path_file, local_path=self.local_path_dir)
def test_upload_async(self):
self._prepare_for_uploading()
def callback():
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
self.client.upload(remote_path=self.remote_path_file, local_path=self.local_path_dir)
def test_copy(self):
self._prepare_for_downloading()
self.client.mkdir(remote_path=self.remote_path_dir2)
self.client.copy(remote_path_from=self.remote_path_file, remote_path_to=self.remote_path_file2)
self.assertTrue(self.client.check(remote_path=self.remote_path_file2))
def test_move(self):
self._prepare_for_downloading()
self.client.mkdir(remote_path=self.remote_path_dir2)
self.client.move(remote_path_from=self.remote_path_file, remote_path_to=self.remote_path_file2)
self.assertFalse(self.client.check(remote_path=self.remote_path_file))
self.assertTrue(self.client.check(remote_path=self.remote_path_file2))
def test_clean(self):
self._prepare_for_downloading()
self.client.clean(remote_path=self.remote_path_dir)
self.assertFalse(self.client.check(remote_path=self.remote_path_file))
self.assertFalse(self.client.check(remote_path=self.remote_path_dir))
def test_info(self):
self._prepare_for_downloading()
result = self.client.info(remote_path=self.remote_path_file)
self.assertEqual(result['size'], '41')
self.assertTrue('created' in result)
self.assertTrue('modified' in result)
def test_directory_is_dir(self):
self._prepare_for_downloading()
self.assertTrue(self.client.is_dir(self.remote_path_dir), 'Should return True for directory')
def test_file_is_not_dir(self):
self._prepare_for_downloading()
self.assertFalse(self.client.is_dir(self.remote_path_file), 'Should return False for file')
def test_get_property_of_non_exist(self):
self._prepare_for_downloading()
result = self.client.get_property(remote_path=self.remote_path_file, option={'name': 'aProperty'})
self.assertEqual(result, None, 'For not found property should return value as None')
def test_set_property(self):
self._prepare_for_downloading()
self.client.set_property(remote_path=self.remote_path_file, option={
'namespace': 'test',
'name': 'aProperty',
'value': 'aValue'
})
result = self.client.get_property(remote_path=self.remote_path_file,
option={'namespace': 'test', 'name': 'aProperty'})
self.assertEqual(result, 'aValue', 'Property value should be set')
def test_set_property_batch(self):
self._prepare_for_downloading()
self.client.set_property_batch(remote_path=self.remote_path_file, option=[
{
'namespace': 'test',
'name': 'aProperty',
'value': 'aValue'
},
{
'namespace': 'test',
'name': 'aProperty2',
'value': 'aValue2'
}
])
result = self.client.get_property(remote_path=self.remote_path_file,
option={'namespace': 'test', 'name': 'aProperty'})
self.assertEqual(result, 'aValue', 'First property value should be set')
result = self.client.get_property(remote_path=self.remote_path_file,
option={'namespace': 'test', 'name': 'aProperty2'})
self.assertEqual(result, 'aValue2', 'Second property value should be set')
def test_pull(self):
self._prepare_for_downloading(True)
self.client.pull(self.remote_path_dir, self.local_path_dir)
self.assertTrue(path.exists(self.local_path_dir), 'Expected the directory is downloaded.')
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.inner_dir_name), 'Expected the directory is downloaded.')
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.inner_dir_name), 'Expected the directory is downloaded.')
self.assertTrue(path.isdir(self.local_path_dir), 'Expected this is a directory.')
self.assertTrue(path.isdir(self.local_path_dir + os.path.sep + self.inner_dir_name), 'Expected this is a directory.')
self.assertTrue(path.exists(self.local_path_dir + os.path.sep + self.local_file),
'Expected the file is downloaded')
self.assertTrue(path.isfile(self.local_path_dir + os.path.sep + self.local_file),
'Expected this is a file')
def test_push(self):
self._prepare_for_uploading()
self.client.push(self.remote_path_dir, self.local_path_dir)
self.assertTrue(self.client.check(self.remote_path_dir), 'Expected the directory is created.')
self.assertTrue(self.client.check(self.remote_path_file), 'Expected the file is uploaded.')
def test_valid(self):
self.assertTrue(self.client.valid())
def test_check_is_overridden(self):
self.assertEqual('GET', self.client.requests['check'])
def test_pull_newer(self):
init_modification_time = int(self._prepare_local_test_file_and_get_modification_time())
sleep(1)
self._prepare_for_downloading(base_path='time/')
result = self.client.pull('time/' + self.remote_path_dir, self.local_path_dir)
update_modification_time = int(os.path.getmtime(self.pulled_file))
self.assertTrue(result)
self.assertGreater(update_modification_time, init_modification_time)
self.client.clean(remote_path='time/' + self.remote_path_dir)
def test_pull_older(self):
self._prepare_for_downloading(base_path='time/')
sleep(1)
init_modification_time = int(self._prepare_local_test_file_and_get_modification_time())
result = self.client.pull('time/' + self.remote_path_dir, self.local_path_dir)
update_modification_time = int(os.path.getmtime(self.pulled_file))
self.assertFalse(result)
self.assertEqual(update_modification_time, init_modification_time)
self.client.clean(remote_path='time/' + self.remote_path_dir)
def test_push_newer(self):
self._prepare_for_downloading(base_path='time/')
sleep(1)
self._prepare_for_uploading()
init_modification_time = self.client.info('time/' + self.remote_path_file)['modified']
result = self.client.push('time/' + self.remote_path_dir, self.local_path_dir)
update_modification_time = self.client.info('time/' + self.remote_path_file)['modified']
self.assertTrue(result)
self.assertNotEqual(init_modification_time, update_modification_time)
self.client.clean(remote_path='time/' + self.remote_path_dir)
def test_push_older(self):
self._prepare_for_uploading()
sleep(1)
self._prepare_for_downloading(base_path='time/')
init_modification_time = self.client.info('time/' + self.remote_path_file)['modified']
result = self.client.push('time/' + self.remote_path_dir, self.local_path_dir)
update_modification_time = self.client.info('time/' + self.remote_path_file)['modified']
self.assertFalse(result)
self.assertEqual(init_modification_time, update_modification_time)
self.client.clean(remote_path='time/' + self.remote_path_dir)
def _prepare_local_test_file_and_get_modification_time(self):
if not path.exists(path=self.local_path_dir):
os.mkdir(self.local_path_dir)
if not path.exists(path=self.local_path_dir + os.sep + self.local_file):
shutil.copy(src=self.local_file_path, dst=self.pulled_file)
return os.path.getmtime(self.pulled_file)
if __name__ == '__main__':
unittest.main()

View file

@ -1,75 +0,0 @@
import unittest
from tests.base_client_it import BaseClientTestCase
from webdav3.client import Resource
from webdav3.urn import Urn
class ResourceTestCase(BaseClientTestCase):
def test_str(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
self.assertEqual('resource /test_dir/test.txt', resource.__str__())
def test_is_not_dir(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
self.assertFalse(resource.is_dir())
def test_is_dir(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_dir))
self.assertTrue(resource.is_dir())
def test_rename(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
resource.rename('new_name.text')
self.assertTrue(self.client.check(self.remote_path_dir + '/new_name.text'))
def test_move(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
self.client.mkdir(self.remote_path_dir2)
resource.move(self.remote_path_file2)
self.assertFalse(self.client.check(self.remote_path_file))
self.assertTrue(self.client.check(self.remote_path_file2))
def test_copy(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
self.client.mkdir(self.remote_path_dir2)
resource.copy(self.remote_path_file2)
self.assertTrue(self.client.check(self.remote_path_file))
self.assertTrue(self.client.check(self.remote_path_file2))
def test_info(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
info = resource.info()
self.assertIsNotNone(info)
self.assertGreater(len(info), 0)
def test_info_params(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
info = resource.info(['size'])
self.assertIsNotNone(info)
self.assertEqual(1, len(info))
self.assertTrue('size' in info)
def test_clean(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
resource.clean()
self.assertFalse(self.client.check(self.remote_path_file))
def test_check(self):
self._prepare_for_downloading()
resource = Resource(self.client, Urn(self.remote_path_file))
self.assertTrue(resource.check())
if __name__ == '__main__':
unittest.main()

View file

@ -1,166 +0,0 @@
# coding=utf-8
import unittest
from unittest import TestCase
from lxml.etree import ElementTree, Element
from webdav3.client import WebDavXmlUtils as utils, listdir
class ClientTestCase(TestCase):
def test_parse_get_list_response(self):
content = '<?xml version="1.0" encoding="utf-8"?><d:multistatus xmlns:d="DAV:"><d:response><d:href>/test_dir/' \
'</d:href><d:propstat><d:status>HTTP/1.1 200 OK</d:status><d:prop><d:resourcetype><d:collection/>' \
'</d:resourcetype><d:getlastmodified>Mon, 16 Oct 2017 04:18:00 GMT</d:getlastmodified>' \
'<d:displayname>test_dir</d:displayname><d:creationdate>2017-10-16T04:18:00Z</d:creationdate>' \
'</d:prop></d:propstat></d:response><d:response><d:href>/test_dir/test.txt/</d:href><d:propstat>' \
'<d:status>HTTP/1.1 200 OK</d:status><d:prop><d:resourcetype><d:collection/></d:resourcetype>' \
'<d:getlastmodified>Mon, 16 Oct 2017 04:18:18 GMT</d:getlastmodified><d:displayname>test.txt' \
'</d:displayname><d:creationdate>2017-10-16T04:18:18Z</d:creationdate></d:prop></d:propstat>' \
'</d:response></d:multistatus>'
result = utils.parse_get_list_response(content.encode('utf-8'))
self.assertEqual(result.__len__(), 2)
def test_create_free_space_request_content(self):
result = utils.create_free_space_request_content()
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propfind xmlns="DAV:"><prop>'
b'<quota-available-bytes/><quota-used-bytes/></prop></propfind>')
def test_parse_free_space_response(self):
content = '<?xml version="1.0" encoding="utf-8"?><d:multistatus xmlns:d="DAV:"><d:response><d:href>/</d:href>' \
'<d:propstat><d:status>HTTP/1.1 200 OK</d:status><d:prop><d:quota-used-bytes>697' \
'</d:quota-used-bytes><d:quota-available-bytes>10737417543</d:quota-available-bytes></d:prop>' \
'</d:propstat></d:response></d:multistatus>'
result = utils.parse_free_space_response(content.encode('utf-8'), 'localhost')
self.assertEqual(result, 10737417543)
def test_parse_info_response(self):
content = '<?xml version="1.0" encoding="utf-8"?><d:multistatus xmlns:d="DAV:"><d:response>' \
'<d:href>/test_dir/test.txt</d:href><d:propstat><d:status>HTTP/1.1 200 OK</d:status><d:prop>' \
'<d:resourcetype/><d:getlastmodified>Wed, 18 Oct 2017 15:16:04 GMT</d:getlastmodified>' \
'<d:getetag>ab0b4b7973803c03639b848682b5f38c</d:getetag><d:getcontenttype>text/plain' \
'</d:getcontenttype><d:getcontentlength>41</d:getcontentlength><d:displayname>test.txt' \
'</d:displayname><d:creationdate>2017-10-18T15:16:04Z</d:creationdate></d:prop></d:propstat>' \
'</d:response></d:multistatus>'
result = utils.parse_info_response(content.encode('utf-8'), '/test_dir/test.txt', 'localhost')
self.assertEqual(result['created'], '2017-10-18T15:16:04Z')
self.assertEqual(result['name'], 'test.txt')
self.assertEqual(result['modified'], 'Wed, 18 Oct 2017 15:16:04 GMT')
self.assertEqual(result['size'], '41')
def test_create_get_property_request_content(self):
option = {
'namespace': 'test',
'name': 'aProperty'
}
result = utils.create_get_property_request_content(option=option, )
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propfind xmlns="DAV:"><prop>'
b'<aProperty xmlns="test"/></prop></propfind>')
def test_create_get_property_request_content_name_only(self):
option = {
'name': 'aProperty'
}
result = utils.create_get_property_request_content(option=option)
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propfind xmlns="DAV:"><prop>'
b'<aProperty xmlns=""/></prop></propfind>')
def test_parse_get_property_response(self):
content = '<?xml version="1.0" encoding="utf-8"?><d:multistatus xmlns:d="DAV:"><d:response>' \
'<d:href>/test_dir/test.txt</d:href><d:propstat><d:status>HTTP/1.1 200 OK</d:status><d:prop>' \
'<aProperty xmlns="test">aValue</aProperty></d:prop></d:propstat></d:response></d:multistatus>'
result = utils.parse_get_property_response(content=content.encode('utf-8'), name='aProperty')
self.assertEqual(result, 'aValue')
def test_create_set_one_property_request_content(self):
option = {
'namespace': 'test',
'name': 'aProperty',
'value': 'aValue'
}
result = utils.create_set_property_batch_request_content(options=[option])
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propertyupdate xmlns="DAV:"><set><prop>'
b'<aProperty xmlns="test">aValue</aProperty></prop></set></propertyupdate>')
def test_create_set_one_property_request_content_name_only(self):
option = {
'name': 'aProperty'
}
result = utils.create_set_property_batch_request_content(options=[option])
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propertyupdate xmlns="DAV:"><set><prop>'
b'<aProperty xmlns=""></aProperty></prop></set></propertyupdate>')
def test_create_set_property_batch_request_content(self):
options = [
{
'namespace': 'test',
'name': 'aProperty',
'value': 'aValue'
},
{
'namespace': 'test2',
'name': 'aProperty2',
'value': 'aValue2'
}
]
result = utils.create_set_property_batch_request_content(options=options)
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propertyupdate xmlns="DAV:"><set><prop>'
b'<aProperty xmlns="test">aValue</aProperty><aProperty2 xmlns="test2">aValue2'
b'</aProperty2></prop></set></propertyupdate>')
def test_create_set_property_batch_request_content_name_only(self):
options = [
{
'name': 'aProperty'
},
{
'name': 'aProperty2'
}
]
result = utils.create_set_property_batch_request_content(options=options)
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<propertyupdate xmlns="DAV:"><set><prop>'
b'<aProperty xmlns=""></aProperty><aProperty2 xmlns=""></aProperty2></prop></set>'
b'</propertyupdate>')
def test_etree_to_string(self):
tree = ElementTree(Element('test'))
result = utils.etree_to_string(tree)
self.assertEqual(result, b'<?xml version=\'1.0\' encoding=\'UTF-8\'?>\n<test/>')
def test_parse_is_dir_response_directory(self):
try:
f = open('./tests/response_dir.xml', encoding='utf-8')
content = f.read().encode('utf-8')
except:
f = open('./tests/response_dir.xml')
content = f.read().decode('utf-8').encode('utf-8')
f.close()
path = '/test_dir'
hostname = 'https://webdav.yandex.ru'
result = utils.parse_is_dir_response(content, path, hostname)
self.assertTrue(result, 'It should be directory')
def test_parse_is_dir_response_file(self):
content = '<?xml version=\'1.0\' encoding=\'UTF-8\'?><d:multistatus xmlns:d="DAV:"><d:response><d:href>/test_' \
'dir/</d:href><d:propstat><d:status>HTTP/1.1 200 OK</d:status><d:prop><d:creationdate>2018-05-10T07' \
':40:11Z</d:creationdate><d:displayname>test_dir</d:displayname><d:getlastmodified>Thu, 10 May 2018' \
' 07:40:11 GMT</d:getlastmodified><d:resourcetype/></d:prop></d:propstat></d:response><d:response><' \
'd:href>/test_dir/test.txt</d:href><d:propstat><d:status>HTTP/1.1 200 OK</d:status><d:prop><d:getet' \
'ag>ab0b4b7973803c03639b848682b5f38c</d:getetag><d:creationdate>2018-05-10T07:40:12Z</d:creationdat' \
'e><d:displayname>test.txt</d:displayname><d:getlastmodified>Thu, 10 May 2018 07:40:12 GMT</d:getla' \
'stmodified><d:getcontenttype>text/plain</d:getcontenttype><d:getcontentlength>41</d:getcontentleng' \
'th><d:resourcetype/></d:prop></d:propstat></d:response></d:multistatus>'
path = '/test_dir/test.txt'
hostname = 'https://webdav.yandex.ru'
result = utils.parse_is_dir_response(content.encode('utf-8'), path, hostname)
self.assertFalse(result, 'It should be file')
def test_listdir_inner_dir(self):
file_names = listdir('.')
self.assertGreater(len(file_names), 0)
self.assertTrue('webdav3/' in file_names)
if __name__ == '__main__':
unittest.main()

View file

@ -1,91 +0,0 @@
import unittest
from webdav3.client import get_options
from webdav3.connection import WebDAVSettings, OptionNotValid, ConnectionSettings
class ConnectionTestCase(unittest.TestCase):
def test_connection_settings_valid(self):
options = {
'webdav_hostname': 'http://localhost:8585',
'webdav_login': 'alice',
'webdav_password': 'secret1234'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertTrue(settings.is_valid())
self.assertTrue(settings.valid())
def test_connection_settings_no_hostname(self):
options = {
'webdav_login': 'alice',
'webdav_password': 'secret1234'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertRaises(OptionNotValid, settings.is_valid)
self.assertFalse(settings.valid())
def test_connection_settings_no_login(self):
options = {
'webdav_hostname': 'http://localhost:8585',
'webdav_password': 'secret1234'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertRaises(OptionNotValid, settings.is_valid)
self.assertFalse(settings.valid())
def test_connection_settings_no_token_and_no_login(self):
options = {
'webdav_hostname': 'http://localhost:8585'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertRaises(OptionNotValid, settings.is_valid)
self.assertFalse(settings.valid())
def test_connection_settings_wrong_cert_path(self):
options = {
'webdav_hostname': 'http://localhost:8585',
'webdav_login': 'alice',
'cert_path': './wrong.file',
'webdav_password': 'secret1234'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertRaises(OptionNotValid, settings.is_valid)
self.assertFalse(settings.valid())
def test_connection_settings_wrong_key_path(self):
options = {
'webdav_hostname': 'http://localhost:8585',
'webdav_login': 'alice',
'key_path': './wrong.file',
'webdav_password': 'secret1234'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertRaises(OptionNotValid, settings.is_valid)
self.assertFalse(settings.valid())
def test_connection_settings_with_key_path_an_no_cert_path(self):
options = {
'webdav_hostname': 'http://localhost:8585',
'webdav_login': 'alice',
'key_path': './publish.sh',
'webdav_password': 'secret1234'
}
webdav_options = get_options(option_type=WebDAVSettings, from_options=options)
settings = WebDAVSettings(webdav_options)
self.assertRaises(OptionNotValid, settings.is_valid)
self.assertFalse(settings.valid())
def test_connection_settings_does_nothing(self):
settings = ConnectionSettings()
settings.is_valid()
self.assertTrue(settings.valid())
if __name__ == '__main__':
unittest.main()

View file

@ -1,23 +0,0 @@
import os
import unittest
from tests.test_client_it import ClientTestCase
class MultiClientTestCase(ClientTestCase):
remote_path_file = 'директория/тестовый.txt'
remote_path_file2 = 'директория/тестовый2.txt'
remote_inner_path_file = 'директория/вложенная/тестовый.txt'
remote_path_dir = 'директория'
remote_path_dir2 = 'директория2'
remote_inner_path_dir = 'директория/вложенная'
inner_dir_name = 'вложенная'
local_base_dir = 'tests/'
local_file = 'тестовый.txt'
local_file_path = local_base_dir + 'тестовый.txt'
local_path_dir = local_base_dir + 'res/директория'
pulled_file = local_path_dir + os.sep + local_file
if __name__ == '__main__':
unittest.main()

View file

@ -1,50 +0,0 @@
import unittest
from webdav3.exceptions import OptionNotValid, LocalResourceNotFound, RemoteResourceNotFound, MethodNotSupported, ConnectionException, NoConnection, \
RemoteParentNotFound, NotConnection, ResponseErrorCode, NotEnoughSpace
class ExceptionsTestCase(unittest.TestCase):
def test_option_not_valid(self):
exception = OptionNotValid('Name', 'Value', 'Namespace/')
self.assertEqual("Option (Namespace/Name=Value) have invalid name or value", exception.__str__())
def test_local_resource_not_found(self):
exception = LocalResourceNotFound('Path')
self.assertEqual("Local file: Path not found", exception.__str__())
def test_remote_resource_not_found(self):
exception = RemoteResourceNotFound('Path')
self.assertEqual("Remote resource: Path not found", exception.__str__())
def test_remote_parent_not_found(self):
exception = RemoteParentNotFound('Path')
self.assertEqual("Remote parent for: Path not found", exception.__str__())
def test_method_not_supported(self):
exception = MethodNotSupported('HEAD', 'Server')
self.assertEqual("Method 'HEAD' not supported for Server", exception.__str__())
def test_connection_exception(self):
exception = ConnectionException(MethodNotSupported('HEAD', 'Server'))
self.assertEqual("Method 'HEAD' not supported for Server", exception.__str__())
def test_no_connection(self):
exception = NoConnection('Server')
self.assertEqual("No connection with Server", exception.__str__())
def test_not_connection_legacy(self):
exception = NotConnection('Server')
self.assertEqual("No connection with Server", exception.__str__())
def test_response_error_code(self):
exception = ResponseErrorCode('http://text/', 502, 'Service Unavailable')
self.assertEqual("Request to http://text/ failed with code 502 and message: Service Unavailable", exception.__str__())
def test_not_enough_space(self):
exception = NotEnoughSpace()
self.assertEqual("Not enough space on the server", exception.__str__())
if __name__ == '__main__':
unittest.main()

View file

@ -1,26 +0,0 @@
import unittest
from tests.test_client_it import ClientTestCase
from webdav3.client import Client
class MultiClientTestCase(ClientTestCase):
options2 = {
'webdav_hostname': 'https://wrong.url.ru',
'webdav_login': 'webdavclient.test2',
'webdav_password': 'Qwerty123!',
'webdav_override_methods': {
'check': 'FAKE',
'download': 'FAKE',
'upload': 'FAKE',
'clean': 'FAKE',
}
}
def setUp(self):
super(ClientTestCase, self).setUp()
self.second_client = Client(self.options2)
if __name__ == '__main__':
unittest.main()

View file

@ -1,26 +0,0 @@
import unittest
from tests.test_client_it import ClientTestCase
class TailingSlashClientTestCase(ClientTestCase):
options = {
'webdav_hostname': 'http://localhost:8585/',
'webdav_login': 'alice',
'webdav_password': 'secret1234',
'webdav_override_methods': {
'check': 'GET'
}
}
def test_list_inner(self):
self._prepare_for_downloading(True)
file_list = self.client.list(self.remote_inner_path_dir)
self.assertIsNotNone(file_list, 'List of files should not be None')
def test_hostname_no_tailing_slash(self):
self.assertEqual('5', self.client.webdav.hostname[-1])
if __name__ == '__main__':
unittest.main()

View file

@ -0,0 +1 @@
__author__ = 'designerror'

View file

@ -0,0 +1,11 @@
__author__ = 'designerror'
import allure
#from hamcrest import *
class TestRequiredOptions:
def test_without_webdav_hostname(self):
options = { 'webdav_server': "https://webdav.yandex.ru"}
allure.attach('options', options.__str__())
assert 1

View file

@ -0,0 +1 @@
__author__ = 'designerror'

View file

@ -1 +0,0 @@
test content for testing of webdav client

2
wdc
View file

@ -306,7 +306,7 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(prog='wdc', formatter_class=Formatter, epilog=epilog, usage=usage) parser = argparse.ArgumentParser(prog='wdc', formatter_class=Formatter, epilog=epilog, usage=usage)
parser.add_argument("action", help=actions_help, choices=actions) parser.add_argument("action", help=actions_help, choices=actions)
from webdav3.client import __version__ as version from webdav.client import __version__ as version
version_text = "{name} {version}".format(name="%(prog)s", version=version) version_text = "{name} {version}".format(name="%(prog)s", version=version)
parser.add_argument("-v", '--version', action='version', version=version_text) parser.add_argument("-v", '--version', action='version', version=version_text)
parser.add_argument("-r", "--root", help="example: dir1/dir2") parser.add_argument("-r", "--root", help="example: dir1/dir2")

File diff suppressed because it is too large Load diff

View file

@ -6,13 +6,10 @@ from webdav3.urn import Urn
class ConnectionSettings: class ConnectionSettings:
def is_valid(self): def is_valid(self):
"""
Method checks is settings are valid
:return: True if settings are valid otherwise False
"""
pass pass
def valid(self): def valid(self):
try: try:
self.is_valid() self.is_valid()
except OptionNotValid: except OptionNotValid:
@ -25,21 +22,20 @@ class WebDAVSettings(ConnectionSettings):
ns = "webdav:" ns = "webdav:"
prefix = "webdav_" prefix = "webdav_"
keys = {'hostname', 'login', 'password', 'token', 'root', 'cert_path', 'key_path', 'recv_speed', 'send_speed', keys = {'hostname', 'login', 'password', 'token', 'root', 'cert_path', 'key_path', 'recv_speed', 'send_speed',
'verbose', 'disable_check', 'override_methods'} 'verbose'}
hostname = None
login = None
password = None
token = None
root = None
cert_path = None
key_path = None
recv_speed = None
send_speed = None
verbose = None
def __init__(self, options): def __init__(self, options):
self.hostname = None
self.login = None
self.password = None
self.token = None
self.root = None
self.cert_path = None
self.key_path = None
self.recv_speed = None
self.send_speed = None
self.verbose = None
self.disable_check = False
self.override_methods = {}
self.options = dict() self.options = dict()
@ -50,9 +46,9 @@ class WebDAVSettings(ConnectionSettings):
self.root = Urn(self.root).quote() if self.root else '' self.root = Urn(self.root).quote() if self.root else ''
self.root = self.root.rstrip(Urn.separate) self.root = self.root.rstrip(Urn.separate)
self.hostname = self.hostname.rstrip(Urn.separate)
def is_valid(self): def is_valid(self):
if not self.hostname: if not self.hostname:
raise OptionNotValid(name="hostname", value=self.hostname, ns=self.ns) raise OptionNotValid(name="hostname", value=self.hostname, ns=self.ns)
@ -70,4 +66,31 @@ class WebDAVSettings(ConnectionSettings):
if not self.token and not self.login: if not self.token and not self.login:
raise OptionNotValid(name="login", value=self.login, ns=self.ns) raise OptionNotValid(name="login", value=self.login, ns=self.ns)
return True
class ProxySettings(ConnectionSettings):
ns = "proxy:"
prefix = "proxy_"
keys = {'hostname', 'login', 'password'}
hostname = None
login = None
password = None
def __init__(self, options):
self.options = dict()
for key in self.keys:
value = options.get(key, '')
self.options[key] = value
self.__dict__[key] = value
def is_valid(self):
if self.password and not self.login:
raise OptionNotValid(name="login", value=self.login, ns=self.ns)
if self.login or self.password:
if not self.hostname:
raise OptionNotValid(name="hostname", value=self.hostname, ns=self.ns)

View file

@ -49,38 +49,34 @@ class RemoteParentNotFound(NotFound):
return "Remote parent for: {path} not found".format(path=self.path) return "Remote parent for: {path} not found".format(path=self.path)
class ResourceTooBig(WebDavException):
def __init__(self, path, size, max_size):
self.path = path
self.size = size
self.max_size = max_size
def __str__(self):
return "Resource {path} is too big, it should be less then {max_size} but actually: {size}".format(
path=self.path,
max_size=self.max_size,
size=self.size)
class MethodNotSupported(WebDavException): class MethodNotSupported(WebDavException):
def __init__(self, name, server): def __init__(self, name, server):
self.name = name self.name = name
self.server = server self.server = server
def __str__(self): def __str__(self):
return "Method '{name}' not supported for {server}".format(name=self.name, server=self.server) return "Method {name} not supported for {server}".format(name=self.name, server=self.server)
class ConnectionException(WebDavException):
def __init__(self, exception):
self.exception = exception
def __str__(self):
return self.exception.__str__()
class NoConnection(WebDavException):
def __init__(self, hostname):
self.hostname = hostname
def __str__(self):
return "No connection with {hostname}".format(hostname=self.hostname)
# This exception left only for supporting original library interface.
class NotConnection(WebDavException): class NotConnection(WebDavException):
def __init__(self, hostname): def __init__(self, hostname):
self.hostname = hostname self.hostname = hostname
def __str__(self): def __str__(self):
return "No connection with {hostname}".format(hostname=self.hostname) return "Not connection with {hostname}".format(hostname=self.hostname)
class ResponseErrorCode(WebDavException): class ResponseErrorCode(WebDavException):
@ -96,7 +92,7 @@ class ResponseErrorCode(WebDavException):
class NotEnoughSpace(WebDavException): class NotEnoughSpace(WebDavException):
def __init__(self): def __init__(self):
self.message = "Not enough space on the server" pass
def __str__(self): def __str__(self):
return self.message return "Not enough space on the server"

View file

@ -1,8 +1,7 @@
try: try:
from urllib.parse import unquote, quote, urlsplit from urllib.parse import unquote, quote
except ImportError: except ImportError:
from urllib import unquote, quote from urllib import unquote, quote
from urlparse import urlsplit
from re import sub from re import sub
@ -34,11 +33,13 @@ class Urn(object):
return self._path return self._path
def filename(self): def filename(self):
path_split = self._path.split(Urn.separate) path_split = self._path.split(Urn.separate)
name = path_split[-2] + Urn.separate if path_split[-1] == '' else path_split[-1] name = path_split[-2] + Urn.separate if path_split[-1] == '' else path_split[-1]
return unquote(name) return unquote(name)
def parent(self): def parent(self):
path_split = self._path.split(Urn.separate) path_split = self._path.split(Urn.separate)
nesting_level = self.nesting_level() nesting_level = self.nesting_level()
parent_path_split = path_split[:nesting_level] parent_path_split = path_split[:nesting_level]
@ -52,14 +53,4 @@ class Urn(object):
return self._path.count(Urn.separate, 0, -1) return self._path.count(Urn.separate, 0, -1)
def is_dir(self): def is_dir(self):
return self._path[-1] == Urn.separate return self._path[-1] == Urn.separate
@staticmethod
def normalize_path(path):
result = sub('/{2,}', '/', path)
return result if len(result) < 1 or result[-1] != Urn.separate else result[:-1]
@staticmethod
def compare_path(path_a, href):
unqouted_path = Urn.separate + unquote(urlsplit(href).path)
return Urn.normalize_path(path_a) == Urn.normalize_path(unqouted_path)