├── yadisk_async ├── py.typed ├── api │ ├── __init__.py │ ├── disk.py │ ├── operations.py │ ├── auth.py │ └── api_request.py ├── objects │ ├── __init__.py │ ├── error_object.py │ ├── operations.py │ ├── auth.py │ ├── yadisk_object.py │ └── disk.py ├── __init__.py ├── settings.py ├── compat.py ├── session.py ├── utils.py ├── exceptions.py └── common.py ├── requirements.txt ├── docs ├── requirements.txt ├── locales │ └── ru │ │ └── LC_MESSAGES │ │ ├── docs.mo │ │ ├── index.mo │ │ ├── intro.mo │ │ ├── changelog.mo │ │ ├── known_issues.mo │ │ ├── index.po │ │ ├── intro.po │ │ ├── known_issues.po │ │ └── changelog.po ├── Makefile ├── index.rst ├── make.bat ├── known_issues.rst ├── docs.rst ├── conf.py ├── intro.rst └── changelog.rst ├── .gitignore ├── tests ├── __init__.py └── yadisk_test.py ├── MANIFEST.in ├── .readthedocs.yaml ├── setup.py ├── COPYING.lesser ├── README.en.rst ├── README.rst ├── README.ru.rst └── COPYING /yadisk_async/py.typed: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aiohttp 2 | aiofiles 3 | -------------------------------------------------------------------------------- /docs/requirements.txt: -------------------------------------------------------------------------------- 1 | sphinx 2 | sphinx-intl 3 | sphinx_rtd_theme 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .*.sw[a-z] 2 | *.py[co] 3 | dist/ 4 | build/ 5 | yadisk_async.egg-info/ 6 | docs/_build 7 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | from .yadisk_test import * 5 | -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/docs.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivknv/yadisk-async/HEAD/docs/locales/ru/LC_MESSAGES/docs.mo -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/index.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivknv/yadisk-async/HEAD/docs/locales/ru/LC_MESSAGES/index.mo -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/intro.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivknv/yadisk-async/HEAD/docs/locales/ru/LC_MESSAGES/intro.mo -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/changelog.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivknv/yadisk-async/HEAD/docs/locales/ru/LC_MESSAGES/changelog.mo -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/known_issues.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ivknv/yadisk-async/HEAD/docs/locales/ru/LC_MESSAGES/known_issues.mo -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include COPYING 2 | include COPYING.lesser 3 | include README.rst 4 | include README.en.rst 5 | include README.ru.rst 6 | include yadisk_async/py.typed 7 | -------------------------------------------------------------------------------- /yadisk_async/api/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # pylint: disable=wildcard-import 4 | from .api_request import * 5 | from .disk import * 6 | from .resources import * 7 | from .operations import * 8 | from .auth import * 9 | -------------------------------------------------------------------------------- /yadisk_async/objects/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # pylint: disable=wildcard-import 4 | from .yadisk_object import * 5 | from .error_object import * 6 | from .disk import * 7 | from .resources import * 8 | from .operations import * 9 | from .auth import * 10 | -------------------------------------------------------------------------------- /yadisk_async/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from . import api, objects, exceptions, utils 4 | from .yadisk import YaDisk 5 | 6 | import warnings 7 | 8 | __version__ = "1.4.5" 9 | 10 | warnings.warn("This library is no longer maintained, use yadisk>=2.0.0 instead, it supports async API", DeprecationWarning) 11 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | 14 | # Build documentation in the docs/ directory with Sphinx 15 | sphinx: 16 | configuration: docs/conf.py 17 | 18 | # Optionally declare the Python requirements required to build your docs 19 | python: 20 | install: 21 | - requirements: requirements.txt 22 | - requirements: docs/requirements.txt 23 | - method: pip 24 | path: . 25 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = python -msphinx 7 | SPHINXPROJ = YaDisk 8 | SOURCEDIR = . 9 | BUILDDIR = _build 10 | 11 | # Put it first so that "make" without argument is like "make help". 12 | help: 13 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 14 | 15 | .PHONY: help Makefile 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. YaDisk documentation master file, created by 2 | sphinx-quickstart on Sun Oct 1 19:06:55 2017. 3 | You can adapt this file completely to your liking, but it should at least 4 | contain the root `toctree` directive. 5 | 6 | Welcome to YaDisk's documentation! 7 | ================================== 8 | 9 | .. _yadisk: https://pypi.org/project/yadisk 10 | 11 | .. warning:: 12 | This library is no longer maintained, use `yadisk`_>=2.0.0 instead, it also 13 | supports async API. 14 | 15 | .. toctree:: 16 | :maxdepth: 2 17 | :caption: Contents: 18 | 19 | intro 20 | known_issues 21 | docs 22 | changelog 23 | 24 | Indices and tables 25 | ================== 26 | 27 | * :ref:`genindex` 28 | * :ref:`modindex` 29 | * :ref:`search` 30 | -------------------------------------------------------------------------------- /yadisk_async/objects/error_object.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .yadisk_object import YaDiskObject 4 | 5 | __all__ = ["ErrorObject"] 6 | 7 | class ErrorObject(YaDiskObject): 8 | """ 9 | Mirrors Yandex.Disk REST API error object. 10 | 11 | :param error: `dict` or `None` 12 | 13 | :ivar message: `str`, human-readable error message 14 | :ivar description: `str`, technical error description 15 | :ivar error: `str`, error code 16 | """ 17 | 18 | def __init__(self, error=None): 19 | YaDiskObject.__init__(self, {"message": str, 20 | "description": str, 21 | "error": str}) 22 | self.set_alias("error_description", "message") 23 | self.import_fields(error) 24 | -------------------------------------------------------------------------------- /yadisk_async/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import aiohttp 4 | 5 | __all__ = ["DEFAULT_TIMEOUT", "DEFAULT_N_RETRIES", "DEFAULT_UPLOAD_TIMEOUT", 6 | "DEFAULT_UPLOAD_RETRY_INTERVAL"] 7 | 8 | # `tuple` of 2 numbers (`int` or float`), default timeout for requests. 9 | # First number is the connect timeout, the second one is the read timeout. 10 | DEFAULT_TIMEOUT = aiohttp.ClientTimeout(sock_connect=10.0, sock_read=15.0) 11 | 12 | # `int`, default number of retries 13 | DEFAULT_N_RETRIES = 3 14 | 15 | # `float`, default retry interval 16 | DEFAULT_RETRY_INTERVAL = 0.0 17 | 18 | # Analogous to `DEFAULT_TIMEOUT` but for `upload` function 19 | DEFAULT_UPLOAD_TIMEOUT = DEFAULT_TIMEOUT 20 | 21 | # Analogous to `DEFAULT_RETRY_INTERVAL` but for `upload` function 22 | DEFAULT_UPLOAD_RETRY_INTERVAL = 0.0 23 | -------------------------------------------------------------------------------- /yadisk_async/compat.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __all__ = ["List", "Dict", "Set", "Callable", "Iterable", "Generator", 4 | "AsyncGenerator", "Coroutine", "Awaitable", "TimeoutError", "AsyncIterable"] 5 | 6 | import sys 7 | 8 | if sys.version_info.major == 3 and sys.version_info.minor < 9: 9 | from typing import ( 10 | List, Dict, Set, Callable, Iterable, Generator, AsyncGenerator, 11 | Coroutine, Awaitable, AsyncIterable 12 | ) 13 | else: 14 | from collections.abc import ( 15 | Callable, Iterable, Generator, AsyncGenerator, Coroutine, Awaitable, 16 | AsyncIterable 17 | ) 18 | 19 | List = list 20 | Dict = dict 21 | Set = set 22 | 23 | if sys.version_info.major == 3 and sys.version_info.minor < 11: 24 | from asyncio import TimeoutError 25 | else: 26 | TimeoutError = TimeoutError 27 | -------------------------------------------------------------------------------- /yadisk_async/session.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | import aiohttp 5 | 6 | from .common import CaseInsensitiveDict 7 | 8 | __all__ = ["SessionWithHeaders"] 9 | 10 | DEFAULT_USER_AGENT = "Python/%s.%s aiohttp/%s" % (sys.version_info.major, 11 | sys.version_info.minor, 12 | aiohttp.__version__) 13 | 14 | 15 | class SessionWithHeaders(aiohttp.ClientSession): 16 | """Just like your regular :any:`aiohttp.ClientSession` but with headers""" 17 | 18 | def __init__(self, *args, **kwargs): 19 | aiohttp.ClientSession.__init__(self, *args, **kwargs) 20 | 21 | self.headers.update(CaseInsensitiveDict({ 22 | "User-Agent": DEFAULT_USER_AGENT, 23 | "Accept-Encoding": ", ".join(("gzip", "deflate")), 24 | "Accept": "*/*", 25 | "Connection": "keep-alive" 26 | })) 27 | 28 | -------------------------------------------------------------------------------- /docs/make.bat: -------------------------------------------------------------------------------- 1 | @ECHO OFF 2 | 3 | pushd %~dp0 4 | 5 | REM Command file for Sphinx documentation 6 | 7 | if "%SPHINXBUILD%" == "" ( 8 | set SPHINXBUILD=python -msphinx 9 | ) 10 | set SOURCEDIR=. 11 | set BUILDDIR=_build 12 | set SPHINXPROJ=YaDisk 13 | 14 | if "%1" == "" goto help 15 | 16 | %SPHINXBUILD% >NUL 2>NUL 17 | if errorlevel 9009 ( 18 | echo. 19 | echo.The Sphinx module was not found. Make sure you have Sphinx installed, 20 | echo.then set the SPHINXBUILD environment variable to point to the full 21 | echo.path of the 'sphinx-build' executable. Alternatively you may add the 22 | echo.Sphinx directory to PATH. 23 | echo. 24 | echo.If you don't have Sphinx installed, grab it from 25 | echo.http://sphinx-doc.org/ 26 | exit /b 1 27 | ) 28 | 29 | %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 30 | goto end 31 | 32 | :help 33 | %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% 34 | 35 | :end 36 | popd 37 | -------------------------------------------------------------------------------- /yadisk_async/api/disk.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .api_request import APIRequest 4 | from ..objects import DiskInfoObject 5 | from ..exceptions import InvalidResponseError 6 | 7 | from typing import Optional, TYPE_CHECKING 8 | from ..compat import Iterable 9 | 10 | if TYPE_CHECKING: 11 | import aiohttp 12 | 13 | __all__ = ["DiskInfoRequest"] 14 | 15 | class DiskInfoRequest(APIRequest): 16 | """ 17 | A request to get disk information. 18 | 19 | :param session: an instance of :any:`aiohttp.ClientSession` with prepared headers 20 | :param fields: list of keys to be included in the response 21 | 22 | :returns: :any:`DiskInfoObject` 23 | """ 24 | 25 | url = "https://cloud-api.yandex.net/v1/disk" 26 | method = "GET" 27 | 28 | def __init__(self, 29 | session: "aiohttp.ClientSession", 30 | fields: Optional[Iterable[str]] = None, **kwargs): 31 | APIRequest.__init__(self, session, {"fields": fields}, **kwargs) 32 | 33 | def process_args(self, fields: Optional[Iterable[str]]) -> None: 34 | if fields is not None: 35 | self.params["fields"] = ",".join(fields) 36 | 37 | def process_json(self, js: Optional[dict]) -> DiskInfoObject: 38 | if js is None: 39 | raise InvalidResponseError("Yandex.Disk returned invalid JSON") 40 | 41 | return DiskInfoObject(js) 42 | -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/index.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2017, Ivan Konovalov 3 | # This file is distributed under the same license as the YaDisk package. 4 | # FIRST AUTHOR , 2017. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: YaDisk 1.3.5\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-12-14 23:07+0500\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 2.14.0\n" 19 | 20 | #: ../../index.rst:15 21 | msgid "Contents:" 22 | msgstr "Содежание:" 23 | 24 | #: ../../index.rst:7 25 | msgid "Welcome to YaDisk's documentation!" 26 | msgstr "Документация YaDisk" 27 | 28 | #: ../../index.rst:12 29 | msgid "" 30 | "This library is no longer maintained, use `yadisk`_>=2.0.0 instead, it " 31 | "also supports async API." 32 | msgstr "" 33 | "Данная библиотека больше не поддерживается, используйте `yadisk`_>=2.0.0 " 34 | "вместо неё, она также поддерживает асинхронный API." 35 | 36 | #: ../../index.rst:25 37 | msgid "Indices and tables" 38 | msgstr "" 39 | 40 | #: ../../index.rst:27 41 | msgid ":ref:`genindex`" 42 | msgstr "" 43 | 44 | #: ../../index.rst:28 45 | msgid ":ref:`modindex`" 46 | msgstr "" 47 | 48 | #: ../../index.rst:29 49 | msgid ":ref:`search`" 50 | msgstr "" 51 | -------------------------------------------------------------------------------- /yadisk_async/objects/operations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from functools import partial 4 | 5 | from .yadisk_object import YaDiskObject 6 | from .resources import LinkObject 7 | from ..common import str_or_error, dict_or_error 8 | 9 | from typing import Optional, TYPE_CHECKING 10 | 11 | if TYPE_CHECKING: 12 | from ..yadisk import YaDisk 13 | 14 | __all__ = ["OperationStatusObject"] 15 | 16 | class OperationStatusObject(YaDiskObject): 17 | """ 18 | Operation status object. 19 | 20 | :param operation_status: `dict` or `None` 21 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 22 | 23 | :ivar type: `str`, type of the operation 24 | :ivar status: `str`, status of the operation 25 | :ivar operation_id: `str`, ID of the operation 26 | :ivar link: :any:`LinkObject`, link to the operation 27 | :ivar data: `dict`, other information about the operation 28 | """ 29 | 30 | def __init__(self, 31 | operation_status: Optional[dict] = None, 32 | yadisk: Optional["YaDisk"] = None): 33 | YaDiskObject.__init__( 34 | self, 35 | {"type": str_or_error, 36 | "status": str_or_error, 37 | "operation_id": str_or_error, 38 | "link": partial(LinkObject, yadisk=yadisk), 39 | "data": dict_or_error}, 40 | yadisk) 41 | 42 | self.import_fields(operation_status) 43 | -------------------------------------------------------------------------------- /docs/known_issues.rst: -------------------------------------------------------------------------------- 1 | Known Issues 2 | ============ 3 | 4 | Very Slow Upload of Certain Types of Files 5 | ########################################## 6 | 7 | .. note:: 8 | 9 | The following information may become outdated at some point in the future. 10 | 11 | Yandex.Disk's REST API limits upload speeds up to 128 KiB/s for certain MIME types of files. 12 | More specifically, throttling takes place based on value of :code:`media_type` 13 | (see :any:`yadisk_async.YaDisk.get_meta`). 14 | It appears there are 3 types of media types that are throttled: 15 | 16 | 1) :code:`data` (.db, .dat, etc.) 17 | 2) :code:`compressed` (.zip, .gz, .tgz, .rar, .etc) 18 | 3) :code:`video` (.3gp, .mp4, .avi, etc.) 19 | 20 | This behavior of throttling is predetermined at the moment of requesting an 21 | upload link (with :any:`yadisk_async.YaDisk.get_upload_link`). The content of the 22 | uploaded file does not matter. 23 | 24 | The reason why this problem cannot be observed when uploading through the 25 | official website, is that this throttling does not apply to internal services 26 | (the Yandex.Disk website uses an intermediate internal API to obtain upload links). 27 | 28 | While it is not clear what the purpose of this throttling is, it is certain at 29 | this point that this is not a bug. 30 | 31 | The only known workaround is to upload files with changed filename extensions (or without them entirely). 32 | For example, if you want to upload a file named "my_database.db", you can initially 33 | upload it under the name "my_database.some_other_extension" and then rename it back 34 | to "my_database.db". This approach has some obvious downsides but at least it 35 | works. 36 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import codecs 5 | import os 6 | 7 | from setuptools import setup, find_packages 8 | 9 | module_dir = os.path.dirname(__file__) 10 | 11 | with codecs.open(os.path.join(module_dir, "README.rst"), encoding="utf8") as f: 12 | long_description = f.read() 13 | 14 | setup(name="yadisk-async", 15 | version="1.4.5", 16 | packages=find_packages(exclude=("tests",)), 17 | description="Библиотека-клиент REST API Яндекс.Диска с поддержкой async/await / Yandex.Disk REST API client library with async/await support", 18 | long_description=long_description, 19 | author="Ivan Konovalov", 20 | author_email="ivknv0@gmail.com", 21 | license="LGPLv3", 22 | python_requires=">=3.8", 23 | install_requires=["aiohttp", "aiofiles"], 24 | url="https://github.com/ivknv/yadisk-async", 25 | project_urls={"Source code": "https://github.com/ivknv/yadisk-async", 26 | "Documentation (EN)": "https://yadisk-async.readthedocs.io/en/latest", 27 | "Documentation (RU)": "https://yadisk-async.readthedocs.io/ru/latest", 28 | "Bug tracker": "https://github.com/ivknv/yadisk-async/issues"}, 29 | classifiers=[ 30 | "Development Status :: 7 - Inactive", 31 | "Intended Audience :: Developers", 32 | "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", 33 | "License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)", 34 | "Programming Language :: Python", 35 | "Programming Language :: Python :: 3", 36 | "Operating System :: OS Independent", 37 | "Topic :: Internet", 38 | "Topic :: Software Development :: Libraries", 39 | "Topic :: Software Development :: Libraries :: Python Modules"], 40 | keywords="yandex yandex.disk rest async") 41 | -------------------------------------------------------------------------------- /yadisk_async/objects/auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .yadisk_object import YaDiskObject 4 | from ..common import str_or_error, int_or_error 5 | 6 | from typing import Optional, TYPE_CHECKING 7 | 8 | if TYPE_CHECKING: 9 | from ..yadisk import YaDisk 10 | 11 | __all__ = ["TokenObject", "TokenRevokeStatusObject"] 12 | 13 | class TokenObject(YaDiskObject): 14 | """ 15 | Token object. 16 | 17 | :param token: `dict` or `None` 18 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 19 | 20 | :ivar access_token: `str`, token string 21 | :ivar refresh_token: `str`, the refresh-token 22 | :ivar token_type: `str`, type of the token 23 | :ivar expires_in: `int`, amount of time before the token expires 24 | """ 25 | 26 | access_token: Optional[str] 27 | refresh_token: Optional[str] 28 | token_type: Optional[str] 29 | expires_in: Optional[int] 30 | 31 | def __init__(self, token: Optional[dict] = None, yadisk: Optional["YaDisk"] = None): 32 | YaDiskObject.__init__( 33 | self, 34 | {"access_token": str_or_error, 35 | "refresh_token": str_or_error, 36 | "token_type": str_or_error, 37 | "expires_in": int_or_error}, 38 | yadisk) 39 | 40 | self.import_fields(token) 41 | 42 | class TokenRevokeStatusObject(YaDiskObject): 43 | """ 44 | Result of token revocation request. 45 | 46 | :param token_revoke_status: `dict` or `None` 47 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 48 | 49 | :ivar status: `str`, status of the operation 50 | """ 51 | 52 | status: Optional[str] 53 | 54 | def __init__(self, 55 | token_revoke_status: Optional[dict]=None, 56 | yadisk: Optional["YaDisk"] = None): 57 | YaDiskObject.__init__(self, {"status": str_or_error}, yadisk) 58 | 59 | self.import_fields(token_revoke_status) 60 | -------------------------------------------------------------------------------- /yadisk_async/api/operations.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from urllib.parse import urlparse, parse_qs, quote 4 | 5 | from .api_request import APIRequest 6 | from ..objects import OperationStatusObject 7 | from ..common import is_operation_link 8 | from ..exceptions import InvalidResponseError 9 | 10 | from typing import Optional, TYPE_CHECKING 11 | from ..compat import Iterable 12 | 13 | if TYPE_CHECKING: 14 | import aiohttp 15 | 16 | __all__ = ["GetOperationStatusRequest"] 17 | 18 | class GetOperationStatusRequest(APIRequest): 19 | """ 20 | A request to get operation status. 21 | 22 | :param session: an instance of :any:`aiohttp.ClientSession` with prepared headers 23 | :param operation_id: operation ID or link 24 | :param fields: list of keys to be included in the response 25 | 26 | :returns: :any:`OperationStatusObject` 27 | """ 28 | 29 | method = "GET" 30 | 31 | def __init__(self, 32 | session: "aiohttp.ClientSession", 33 | operation_id: str, 34 | fields: Optional[Iterable[str]] = None, **kwargs): 35 | if is_operation_link(operation_id): 36 | parsed_url = urlparse(operation_id) 37 | self.url = "https://" + parsed_url.netloc + parsed_url.path 38 | 39 | params = parse_qs(parsed_url.query) 40 | 41 | if fields is None: 42 | fields = params.get("fields", [None])[0] 43 | else: 44 | operation_id = quote(operation_id) 45 | self.url = "https://cloud-api.yandex.net/v1/disk/operations/%s" % (operation_id,) 46 | 47 | APIRequest.__init__(self, session, {"fields": fields}, **kwargs) 48 | 49 | def process_args(self, fields: Optional[Iterable[str]]) -> None: 50 | if fields is not None: 51 | self.params["fields"] = ",".join(fields) 52 | 53 | def process_json(self, js: Optional[dict]) -> OperationStatusObject: 54 | if js is None: 55 | raise InvalidResponseError("Yandex.Disk returned invalid JSON") 56 | 57 | if "items" in js and isinstance(js["items"], list): 58 | if js["items"]: 59 | return OperationStatusObject(js["items"][0]) 60 | 61 | return OperationStatusObject(js) 62 | -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/intro.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2017, Ivan Konovalov 3 | # This file is distributed under the same license as the YaDisk package. 4 | # FIRST AUTHOR , 2017. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: YaDisk 1.3.5\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-12-14 23:07+0500\n" 12 | "PO-Revision-Date: 2018-01-05 22:45+0500\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 2.14.0\n" 19 | 20 | #: ../../intro.rst:2 21 | msgid "Introduction" 22 | msgstr "Введение" 23 | 24 | #: ../../intro.rst:6 25 | msgid "" 26 | "YaDisk-async is a modified version of `YaDisk`_ with :code:`async/await` " 27 | "support. It uses `aiohttp` instead of `requests`." 28 | msgstr "" 29 | "YaDisk-async - это модифицированная версия `YaDisk`_ с поддержкой " 30 | ":code:`async/await`, использует `aiohttp` вместо `requests`." 31 | 32 | #: ../../intro.rst:10 33 | msgid "" 34 | "This library is no longer maintained, use `yadisk " 35 | "`__>=2.0.0 instead, it also supports " 36 | "async API." 37 | msgstr "Данная библиотека больше не поддерживается, используйте `yadisk " 38 | "`__>=2.0.0 вместо неё, она также поддерживает " 39 | "асинхронный API." 40 | 41 | #: ../../intro.rst:13 42 | msgid "" 43 | "The usage is more or less the same, except that you have to manually " 44 | "close all the sessions (can be done with :any:`YaDisk.close` or through " 45 | ":code:`async with` statement)." 46 | msgstr "" 47 | "Использование библиотеки почти не отличается, не считая того, что " 48 | "закрывать все сессии нужно вручную (может быть сделано с помощью " 49 | ":any:`YaDisk.close` или через конструкцию :code:`async with`)." 50 | 51 | #: ../../intro.rst:17 52 | msgid "Installation" 53 | msgstr "Установка" 54 | 55 | #: ../../intro.rst:23 56 | msgid "or" 57 | msgstr "или" 58 | 59 | #: ../../intro.rst:30 60 | msgid "Examples" 61 | msgstr "Примеры" 62 | 63 | #: ../../intro.rst:77 64 | msgid "Receiving token with confirmation code" 65 | msgstr "Получение токена через код подтверждения" 66 | 67 | #: ../../intro.rst:109 68 | msgid "Recursive upload" 69 | msgstr "Рекурсивная загрузка файлов" 70 | 71 | #: ../../intro.rst:184 72 | msgid "Setting custom properties of files" 73 | msgstr "Задание пользовательских свойств файлов" 74 | 75 | #: ../../intro.rst:215 76 | msgid "Emptying the trash bin" 77 | msgstr "Очищение корзины" 78 | 79 | #~ msgid "YaDisk is a Yandex.Disk REST API client library." 80 | #~ msgstr "YaDisk - это библиотека-клиент REST API Яндекс.Диска." 81 | 82 | -------------------------------------------------------------------------------- /docs/docs.rst: -------------------------------------------------------------------------------- 1 | 2 | Documentation 3 | ============= 4 | 5 | YaDisk object 6 | ############# 7 | 8 | .. autoclass:: yadisk_async.YaDisk 9 | :members: 10 | 11 | General parameters 12 | ################## 13 | 14 | Almost all methods of `YaDisk` (the ones that accept `**kwargs`) accept some additional arguments: 15 | 16 | * **n_retries** - `int`, maximum number of retries for a request 17 | * **retry_interval** - `float`, delay between retries (in seconds) 18 | * **headers** - `dict` or `None`, additional request headers 19 | 20 | `aiohttp` parameters like `timeout`, `proxies`, etc. are also accepted (see :py:func:`aiohttp.request`). 21 | 22 | This also applies to low-level functions and API request objects as well. 23 | 24 | Settings 25 | ######## 26 | 27 | The following settings can be accessed and changed at runtime in `yadisk_async.settings` module: 28 | 29 | * **DEFAULT_TIMEOUT** - :py:class:`aiohttp.ClientTimeout`, default timeout for requests. 30 | * **DEFAULT_N_RETRIES** - `int`, default number of retries 31 | * **DEFAULT_RETRY_INTERVAL** - `float`, default retry interval 32 | * **DEFAULT_UPLOAD_TIMEOUT** - analogous to `DEFAULT_TIMEOUT` but for `upload` function 33 | * **DEFAULT_UPLOAD_RETRY_INTERVAL** - analogous to `DEFAULT_RETRY_INTERVAL` but for `upload` function 34 | 35 | Exceptions 36 | ########## 37 | 38 | Aside from the exceptions listed below, API requests can also raise exceptions in `aiohttp`. 39 | 40 | .. automodule:: yadisk_async.exceptions 41 | :members: 42 | :show-inheritance: 43 | 44 | Objects 45 | ####### 46 | 47 | .. automodule:: yadisk_async.objects 48 | 49 | .. autoclass:: YaDiskObject 50 | :members: 51 | 52 | .. autoclass:: ErrorObject 53 | :members: 54 | :show-inheritance: 55 | 56 | .. automodule:: yadisk_async.objects.auth 57 | :members: 58 | :show-inheritance: 59 | 60 | .. automodule:: yadisk_async.objects.disk 61 | :members: 62 | :show-inheritance: 63 | 64 | .. automodule:: yadisk_async.objects.resources 65 | :members: 66 | :inherited-members: 67 | :show-inheritance: 68 | :exclude-members: import_fields,set_alias,remove_alias,remove_field,set_field_type,set_field_types 69 | 70 | .. automodule:: yadisk_async.objects.operations 71 | :members: 72 | :show-inheritance: 73 | 74 | Low-level API 75 | ############# 76 | 77 | Utilities 78 | ********* 79 | 80 | .. automodule:: yadisk_async.utils 81 | :members: 82 | 83 | API request objects 84 | ******************* 85 | 86 | .. automodule:: yadisk_async.api 87 | 88 | .. autoclass:: APIRequest 89 | :members: 90 | 91 | .. automodule:: yadisk_async.api.auth 92 | :members: 93 | :show-inheritance: 94 | 95 | .. automodule:: yadisk_async.api.disk 96 | :members: 97 | :show-inheritance: 98 | 99 | .. automodule:: yadisk_async.api.resources 100 | :members: 101 | :show-inheritance: 102 | 103 | .. automodule:: yadisk_async.api.operations 104 | :members: 105 | :show-inheritance: 106 | -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/known_issues.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2023, Ivan Konovalov 3 | # This file is distributed under the same license as the YaDisk package. 4 | # FIRST AUTHOR , 2023. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: YaDisk 1.3.5\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-01-30 18:45+0500\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 2.11.0\n" 19 | 20 | #: ../../known_issues.rst:2 21 | msgid "Known Issues" 22 | msgstr "Известные проблемы" 23 | 24 | #: ../../known_issues.rst:5 25 | msgid "Very Slow Upload of Certain Types of Files" 26 | msgstr "Очень медленная загрузка некоторых типов файлов на Яндекс.Диск" 27 | 28 | #: ../../known_issues.rst:9 29 | msgid "The following information may become outdated at some point in the future." 30 | msgstr "Данная информация может в дальнейшем будущем оказаться устаревшей." 31 | 32 | #: ../../known_issues.rst:11 33 | msgid "" 34 | "Yandex.Disk's REST API limits upload speeds up to 128 KiB/s for certain " 35 | "MIME types of files. More specifically, throttling takes place based on " 36 | "value of :code:`media_type` (see :any:`yadisk_async.YaDisk.get_meta`). It" 37 | " appears there are 3 types of media types that are throttled:" 38 | msgstr "" 39 | "REST API Яндекс.Диск ограничивает скорость загрузки файлов на Диск до 128" 40 | " KiB/s для определенных MIME типов файлов. Если быть точнее, троттлинг " 41 | "осуществляется в зависимости от значения :code:`media_type` (см. " 42 | ":any:`yadisk_async.YaDisk.get_meta`). Судя по всему ограничение скорости " 43 | "действует на 3 типа файлов (media type):" 44 | 45 | #: ../../known_issues.rst:16 46 | msgid ":code:`data` (.db, .dat, etc.)" 47 | msgstr "" 48 | 49 | #: ../../known_issues.rst:17 50 | msgid ":code:`compressed` (.zip, .gz, .tgz, .rar, .etc)" 51 | msgstr "" 52 | 53 | #: ../../known_issues.rst:18 54 | msgid ":code:`video` (.3gp, .mp4, .avi, etc.)" 55 | msgstr "" 56 | 57 | #: ../../known_issues.rst:20 58 | msgid "" 59 | "This behavior of throttling is predetermined at the moment of requesting " 60 | "an upload link (with :any:`yadisk_async.YaDisk.get_upload_link`). The " 61 | "content of the uploaded file does not matter." 62 | msgstr "" 63 | "Ограничение скорости предопределяется в момент получения ссылки для " 64 | "загрузки файла на диск (см. :any:`yadisk_async.YaDisk.get_upload_link`). " 65 | "Содержимое загружаемого файла не имеет значения." 66 | 67 | #: ../../known_issues.rst:24 68 | msgid "" 69 | "The reason why this problem cannot be observed when uploading through the" 70 | " official website, is that this throttling does not apply to internal " 71 | "services (the Yandex.Disk website uses an intermediate internal API to " 72 | "obtain upload links)." 73 | msgstr "" 74 | "Причина, по которой эта проблема не наблюдается при попытке загрузить " 75 | "файл через официальный сайт, заключается в том, что ограничение скорости" 76 | " не применяется для внутренних сервисов (сайт Яндекс.Диска использует " 77 | "промежуточный внутренний API для получения ссылок)." 78 | 79 | #: ../../known_issues.rst:28 80 | msgid "" 81 | "While it is not clear what the purpose of this throttling is, it is " 82 | "certain at this point that this is not a bug." 83 | msgstr "Хотя и не понятно, в чем смысл такого ограничения, это точно не баг." 84 | 85 | #: ../../known_issues.rst:31 86 | msgid "" 87 | "The only known workaround is to upload files with changed filename " 88 | "extensions (or without them entirely). For example, if you want to upload" 89 | " a file named \"my_database.db\", you can initially upload it under the " 90 | "name \"my_database.some_other_extension\" and then rename it back to " 91 | "\"my_database.db\". This approach has some obvious downsides but at least" 92 | " it works." 93 | msgstr "" 94 | "Единственный известный способ обхода данной проблемы - это загрузка " 95 | "файлов с измененным расширением (или без расширения). Например, если вы " 96 | "хотите загрузить на Диск файл \"my_database.db\", вы можете изначально " 97 | "загрузить его под именем \"my_database.some_other_extension\" и после " 98 | "загрузки переименовать обратно в \"my_database.db\". У такого подхода " 99 | "есть очевидные недостатки, но по крайней мере он работает." 100 | -------------------------------------------------------------------------------- /yadisk_async/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from collections import defaultdict 4 | import asyncio 5 | 6 | import aiohttp 7 | 8 | from .objects import ErrorObject 9 | from .exceptions import * 10 | from . import settings 11 | 12 | from typing import Optional, Union, TypeVar, Protocol 13 | 14 | from .compat import Callable, Awaitable, TimeoutError 15 | 16 | __all__ = ["get_exception", "auto_retry"] 17 | 18 | EXCEPTION_MAP = {400: defaultdict(lambda: BadRequestError, 19 | {"FieldValidationError": FieldValidationError}), 20 | 401: defaultdict(lambda: UnauthorizedError), 21 | 403: defaultdict(lambda: ForbiddenError), 22 | 404: defaultdict(lambda: NotFoundError, 23 | {"DiskNotFoundError": PathNotFoundError, 24 | "DiskOperationNotFoundError": OperationNotFoundError}), 25 | 406: defaultdict(lambda: NotAcceptableError), 26 | 409: defaultdict(lambda: ConflictError, 27 | {"DiskPathDoesntExistsError": ParentNotFoundError, 28 | "DiskPathPointsToExistentDirectoryError": DirectoryExistsError, 29 | "DiskResourceAlreadyExistsError": PathExistsError, 30 | "MD5DifferError": MD5DifferError}), 31 | 415: defaultdict(lambda: UnsupportedMediaError), 32 | 423: defaultdict(lambda: LockedError, 33 | {"DiskResourceLockedError": ResourceIsLockedError, 34 | "DiskUploadTrafficLimitExceeded": UploadTrafficLimitExceededError}), 35 | 429: defaultdict(lambda: TooManyRequestsError), 36 | 500: defaultdict(lambda: InternalServerError), 37 | 502: defaultdict(lambda: BadGatewayError), 38 | 503: defaultdict(lambda: UnavailableError), 39 | 504: defaultdict(lambda: GatewayTimeoutError), 40 | 507: defaultdict(lambda: InsufficientStorageError)} 41 | 42 | async def get_exception(response: aiohttp.client.ClientResponse) -> YaDiskError: 43 | """ 44 | Get an exception instance based on response, assuming the request has failed. 45 | 46 | :param response: an instance of :any:`aiohttp.ClientResponse` 47 | 48 | :returns: an exception instance, subclass of :any:`YaDiskError` 49 | """ 50 | 51 | exc_group = EXCEPTION_MAP.get(response.status, None) 52 | 53 | if exc_group is None: 54 | return UnknownYaDiskError("Unknown Yandex.Disk error") 55 | 56 | try: 57 | js = await response.json() 58 | except (ValueError, RuntimeError): 59 | js = None 60 | 61 | error = ErrorObject(js) 62 | 63 | msg = error.message or "" 64 | desc = error.description or "" 65 | 66 | exc = exc_group[error.error] 67 | 68 | return exc(error.error, "%s (%s / %s)" % (msg, desc, error.error), response) 69 | 70 | T = TypeVar("T") 71 | 72 | async def auto_retry(func: Callable[[], Union[T, Awaitable[T]]], 73 | n_retries: Optional[int] = None, 74 | retry_interval: Optional[Union[int, float]] = None) -> T: 75 | """ 76 | Attempt to perform a request with automatic retries. 77 | A retry is triggered by :any:`aiohttp.ClientError` or :any:`RetriableYaDiskError`. 78 | 79 | :param func: function to run, must not require any arguments 80 | :param n_retries: `int`, maximum number of retries 81 | :param retry_interval: `int` or `float`, delay between retries (in seconds) 82 | 83 | :returns: return value of func() 84 | """ 85 | 86 | if n_retries is None: 87 | n_retries = settings.DEFAULT_N_RETRIES 88 | 89 | if retry_interval is None: 90 | retry_interval = settings.DEFAULT_RETRY_INTERVAL 91 | 92 | for i in range(n_retries + 1): 93 | try: 94 | if asyncio.iscoroutinefunction(func): 95 | return await func() 96 | else: 97 | return func() 98 | except (aiohttp.ClientError, TimeoutError, RetriableYaDiskError) as e: 99 | if i == n_retries: 100 | raise e 101 | 102 | if retry_interval: 103 | await asyncio.sleep(retry_interval) 104 | 105 | # This should never be reachable 106 | assert False 107 | -------------------------------------------------------------------------------- /yadisk_async/objects/yadisk_object.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from typing import Optional, Callable, Any, Iterator, TYPE_CHECKING 4 | 5 | if TYPE_CHECKING: 6 | from ..yadisk import YaDisk 7 | 8 | __all__ = ["YaDiskObject"] 9 | 10 | class YaDiskObject: 11 | """ 12 | Base class for all objects mirroring the ones returned by Yandex.Disk REST API. 13 | It must have a fixed number of fields, each field must have a type. 14 | It also supports subscripting and access of fields through the . operator. 15 | 16 | :param field_types: `dict` or `None` 17 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 18 | """ 19 | 20 | FIELD_TYPES: dict 21 | FIELDS: dict 22 | ALIASES: dict 23 | _yadisk: Optional["YaDisk"] 24 | 25 | def __init__(self, 26 | field_types: Optional[dict] = None, 27 | yadisk: Optional["YaDisk"] = None): 28 | if field_types is None: 29 | field_types = {} 30 | 31 | self.FIELD_TYPES = {} 32 | self.FIELDS = {} 33 | self.ALIASES = {} 34 | self.set_field_types(field_types) 35 | 36 | self._yadisk = yadisk 37 | 38 | def set_field_types(self, field_types: dict) -> None: 39 | """ 40 | Set the field types of the object 41 | 42 | :param field_types: `dict`, where keys are the field names and values are types (or factories) 43 | """ 44 | 45 | self.FIELD_TYPES = field_types 46 | 47 | for field in field_types.keys(): 48 | self[field] = None 49 | 50 | def set_field_type(self, field: str, type: Callable) -> None: 51 | """ 52 | Set field type. 53 | 54 | :param field: `str` 55 | :param type: type or factory 56 | """ 57 | 58 | self.FIELD_TYPES[field] = type 59 | self[field] = None 60 | 61 | def set_alias(self, alias: str, name: str) -> None: 62 | """ 63 | Set an alias. 64 | 65 | :param alias: `str`, alias to add 66 | :param name: `str`, field name 67 | """ 68 | 69 | self.ALIASES[alias] = name 70 | 71 | def remove_alias(self, alias: str) -> None: 72 | """ 73 | Remove an alias. 74 | 75 | :param alias: `str` 76 | """ 77 | 78 | self.ALIASES.pop(alias) 79 | 80 | def remove_field(self, field: str) -> None: 81 | """ 82 | Remove field. 83 | 84 | :param field: `str` 85 | """ 86 | 87 | self.FIELDS.pop(field) 88 | self.FIELD_TYPES.pop(field) 89 | 90 | def import_fields(self, source_dict: Optional[dict]) -> None: 91 | """ 92 | Set all the fields of the object to the values in `source_dict`. 93 | All the other fields are ignored 94 | 95 | :param source_dict: `dict` or `None` (nothing will be done in that case) 96 | """ 97 | 98 | if source_dict is not None: 99 | for field in self.FIELDS: 100 | try: 101 | self[field] = source_dict[field] 102 | except KeyError: 103 | pass 104 | 105 | for alias, field in self.ALIASES.items(): 106 | try: 107 | self[field] = source_dict[alias] 108 | except KeyError: 109 | pass 110 | 111 | def __setattr__(self, attr: str, value: Any) -> None: 112 | if attr in ("FIELDS", "FIELD_TYPES", "ALIASES", "_yadisk"): 113 | self.__dict__[attr] = value 114 | return 115 | 116 | attr = self.ALIASES.get(attr, attr) 117 | 118 | if attr not in self.FIELD_TYPES: 119 | raise AttributeError("Unknown attribute: %r" % (attr,)) 120 | 121 | datatype = self.FIELD_TYPES[attr] 122 | self.FIELDS[attr] = datatype(value) if value is not None else None 123 | 124 | def __getattr__(self, attr: str) -> Any: 125 | attr = self.ALIASES.get(attr, attr) 126 | 127 | if attr not in self.FIELD_TYPES: 128 | raise AttributeError("Unknown attribute: %r" % (attr,)) 129 | 130 | return self.FIELDS[attr] 131 | 132 | def __getitem__(self, key: str) -> Any: 133 | return self.FIELDS[key] 134 | 135 | def __setitem__(self, key: str, value: Any) -> None: 136 | self.__setattr__(key, value) 137 | 138 | def __delitem__(self, key: str) -> None: 139 | del self.FIELDS[key] 140 | 141 | def __iter__(self) -> Iterator[dict]: 142 | return iter(self.FIELDS) 143 | 144 | def __len__(self) -> int: 145 | return len(self.FIELDS) 146 | 147 | def __repr__(self) -> str: 148 | return "<%s%r>" % (self.__class__.__name__, self.FIELDS) 149 | -------------------------------------------------------------------------------- /yadisk_async/exceptions.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | __all__ = ["YaDiskError", "RetriableYaDiskError", "UnknownYaDiskError", 4 | "WrongResourceTypeError", "BadRequestError", "UnauthorizedError", 5 | "ForbiddenError", "NotFoundError", "NotAcceptableError", 6 | "ConflictError", "PayloadTooLargeError", "UnsupportedMediaError", 7 | "LockedError", "UploadTrafficLimitExceededError", "TooManyRequestsError", 8 | "InternalServerError", "BadGatewayError", "UnavailableError", 9 | "GatewayTimeoutError", "InsufficientStorageError", "PathNotFoundError", 10 | "ParentNotFoundError", "PathExistsError", "DirectoryExistsError", 11 | "FieldValidationError", "ResourceIsLockedError", "MD5DifferError", 12 | "OperationNotFoundError", "InvalidResponseError"] 13 | 14 | class YaDiskError(Exception): 15 | """ 16 | Base class for all exceptions in this library. 17 | 18 | :ivar error_type: `str`, unique error code as returned by API 19 | :ivar response: an instance of :any:`aiohttp.ClientResponse` 20 | 21 | :param error_type: `str`, unique error code as returned by API 22 | :param msg: `str`, exception message 23 | :param response: an instance of :any:`aiohttp.ClientResponse` 24 | """ 25 | 26 | def __init__(self, error_type=None, msg="", response=None): 27 | Exception.__init__(self, msg) 28 | 29 | self.error_type = error_type 30 | self.response = response 31 | 32 | class WrongResourceTypeError(YaDiskError): 33 | """Thrown when the resource was expected to be of different type (e.g., file instead of directory).""" 34 | 35 | def __init__(self, msg=""): 36 | YaDiskError.__init__(self, None, msg, None) 37 | 38 | class RetriableYaDiskError(YaDiskError): 39 | """Thrown when there was an error but it would make sense to retry the request.""" 40 | pass 41 | 42 | class UnknownYaDiskError(RetriableYaDiskError): 43 | """Thrown when the request failed but the response does not contain any error info.""" 44 | 45 | def __init__(self, msg="", response=None): 46 | RetriableYaDiskError.__init__(self, None, msg, response) 47 | 48 | class BadRequestError(YaDiskError): 49 | """Thrown when the server returns code 400.""" 50 | pass 51 | 52 | class UnauthorizedError(YaDiskError): 53 | """Thrown when the server returns code 401.""" 54 | pass 55 | 56 | class ForbiddenError(YaDiskError): 57 | """Thrown when the server returns code 403.""" 58 | pass 59 | 60 | class NotFoundError(YaDiskError): 61 | """Thrown when the server returns code 404.""" 62 | pass 63 | 64 | class NotAcceptableError(YaDiskError): 65 | """Thrown when the server returns code 406.""" 66 | pass 67 | 68 | class ConflictError(YaDiskError): 69 | """Thrown when the server returns code 409.""" 70 | pass 71 | 72 | class PayloadTooLargeError(YaDiskError): 73 | """Thrown when the server returns code 413.""" 74 | pass 75 | 76 | class UnsupportedMediaError(YaDiskError): 77 | """Thrown when the server returns code 415.""" 78 | pass 79 | 80 | class LockedError(YaDiskError): 81 | """Thrown when the server returns code 423.""" 82 | pass 83 | 84 | class UploadTrafficLimitExceededError(LockedError): 85 | """Thrown when upload limit has been exceeded.""" 86 | pass 87 | 88 | class TooManyRequestsError(YaDiskError): 89 | """Thrown when the server returns code 429.""" 90 | pass 91 | 92 | class InternalServerError(RetriableYaDiskError): 93 | """Thrown when the server returns code 500.""" 94 | pass 95 | 96 | class BadGatewayError(RetriableYaDiskError): 97 | """Thrown when the server returns code 502""" 98 | pass 99 | 100 | class UnavailableError(RetriableYaDiskError): 101 | """Thrown when the server returns code 503.""" 102 | pass 103 | 104 | class GatewayTimeoutError(RetriableYaDiskError): 105 | """Thrown when the server returns code 504""" 106 | pass 107 | 108 | class InsufficientStorageError(YaDiskError): 109 | """Thrown when the server returns code 507.""" 110 | pass 111 | 112 | class PathNotFoundError(NotFoundError): 113 | """Thrown when the requested path does not exist.""" 114 | pass 115 | 116 | class ParentNotFoundError(ConflictError): 117 | """Thrown by `mkdir`, `upload`, etc. when the parent directory doesn't exist.""" 118 | pass 119 | 120 | class PathExistsError(ConflictError): 121 | """Thrown when the requested path already exists.""" 122 | pass 123 | 124 | class DirectoryExistsError(PathExistsError): 125 | """Thrown when the directory already exists.""" 126 | pass 127 | 128 | class FieldValidationError(BadRequestError): 129 | """Thrown when the request contains fields with invalid data.""" 130 | pass 131 | 132 | class ResourceIsLockedError(LockedError): 133 | """Thrown when the resource is locked by another operation.""" 134 | pass 135 | 136 | class MD5DifferError(ConflictError): 137 | """Thrown when the MD5 hash of the file to be deleted doesn't match with the actual one.""" 138 | pass 139 | 140 | class OperationNotFoundError(NotFoundError): 141 | """Thrown by `get_operation_status()` when the operation doesn't exist.""" 142 | pass 143 | 144 | class InvalidResponseError(YaDiskError): 145 | """Thrown when Yandex.Disk did not return a JSON response or if it's invalid.""" 146 | pass 147 | -------------------------------------------------------------------------------- /yadisk_async/api/auth.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from .api_request import APIRequest 4 | from ..objects import TokenObject, TokenRevokeStatusObject 5 | from ..exceptions import InvalidResponseError 6 | 7 | from typing import Optional, TYPE_CHECKING 8 | 9 | if TYPE_CHECKING: 10 | import aiohttp 11 | 12 | __all__ = ["RefreshTokenRequest", "RevokeTokenRequest", "GetTokenRequest"] 13 | 14 | class RefreshTokenRequest(APIRequest): 15 | """ 16 | A request to refresh an existing token. 17 | 18 | :param session: an instance of :any:`aiohttp.ClientSession` with prepared headers 19 | :param refresh_token: the refresh token that was received with the original token 20 | :param client_id: application ID 21 | :param client_secret: application secret password 22 | 23 | :returns: :any:`TokenObject` 24 | """ 25 | 26 | url = "https://oauth.yandex.ru/token" 27 | method = "POST" 28 | 29 | def __init__(self, 30 | session: "aiohttp.ClientSession", 31 | refresh_token: str, 32 | client_id: str, 33 | client_secret: str, **kwargs): 34 | APIRequest.__init__(self, session, {"refresh_token": refresh_token, 35 | "client_id": client_id, 36 | "client_secret": client_secret}, **kwargs) 37 | 38 | def process_args(self, refresh_token: str, client_id: str, client_secret: str) -> None: 39 | self.data["grant_type"] = "refresh_token" 40 | self.data["refresh_token"] = refresh_token 41 | self.data["client_id"] = client_id 42 | self.data["client_secret"] = client_secret 43 | 44 | def process_json(self, js: Optional[dict]) -> TokenObject: 45 | if js is None: 46 | raise InvalidResponseError("Yandex.Disk did not return valid JSON") 47 | 48 | return TokenObject(js) 49 | 50 | class RevokeTokenRequest(APIRequest): 51 | """ 52 | A request to revoke the token. 53 | 54 | :param session: an instance of :any:`aiohttp.ClientSession` with prepared headers 55 | :param token: the token to be revoked 56 | :param client_id: application ID 57 | :param client_secret: application secret password 58 | 59 | :returns: :any:`TokenRevokeStatusObject` 60 | """ 61 | 62 | url = "https://oauth.yandex.ru/revoke_token" 63 | method = "POST" 64 | 65 | def __init__(self, 66 | session: "aiohttp.ClientSession", 67 | token: str, 68 | client_id: str, 69 | client_secret: str, **kwargs): 70 | APIRequest.__init__(self, session, {"token": token, 71 | "client_id": client_id, 72 | "client_secret": client_secret}, **kwargs) 73 | 74 | def process_args(self, token: str, client_id: str, client_secret: str) -> None: 75 | self.data["access_token"] = token 76 | self.data["client_id"] = client_id 77 | self.data["client_secret"] = client_secret 78 | 79 | def process_json(self, js: Optional[dict]) -> TokenRevokeStatusObject: 80 | if js is None: 81 | raise InvalidResponseError("Yandex.Disk did not return valid JSON") 82 | 83 | return TokenRevokeStatusObject(js) 84 | 85 | class GetTokenRequest(APIRequest): 86 | """ 87 | A request to get the token. 88 | 89 | :param session: an instance of :any:`aiohttp.ClientSession` with prepared headers 90 | :param code: confirmation code 91 | :param client_id: application ID 92 | :param client_secret: application secret password 93 | :param device_id: unique device ID (between 6 and 50 characters) 94 | 95 | :returns: :any:`TokenObject` 96 | """ 97 | 98 | url = "https://oauth.yandex.ru/token" 99 | method = "POST" 100 | 101 | def __init__(self, 102 | session: "aiohttp.ClientSession", 103 | code: str, 104 | client_id: str, 105 | client_secret: str, 106 | device_id: Optional[str] = None, 107 | device_name: Optional[str] = None, **kwargs): 108 | APIRequest.__init__(self, session, {"code": code, 109 | "client_id": client_id, 110 | "client_secret": client_secret, 111 | "device_id": device_id, 112 | "device_name": device_name}, **kwargs) 113 | 114 | def process_args(self, 115 | code: str, 116 | client_id: str, 117 | client_secret: str, 118 | device_id: Optional[str], 119 | device_name: Optional[str]) -> None: 120 | self.data["grant_type"] = "authorization_code" 121 | self.data["code"] = code 122 | self.data["client_id"] = client_id 123 | self.data["client_secret"] = client_secret 124 | 125 | if device_id is not None: 126 | self.data["device_id"] = device_id 127 | 128 | if device_name is not None: 129 | self.data["device_name"] = device_name 130 | 131 | def process_json(self, js: Optional[dict]) -> TokenObject: 132 | if js is None: 133 | raise InvalidResponseError("Yandex.Disk did not return valid JSON") 134 | 135 | return TokenObject(js) 136 | -------------------------------------------------------------------------------- /yadisk_async/api/api_request.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import aiohttp 4 | 5 | from ..exceptions import InvalidResponseError 6 | 7 | from ..utils import auto_retry, get_exception 8 | from ..common import CaseInsensitiveDict 9 | from .. import settings 10 | 11 | from typing import Optional, Union, TypeVar 12 | from ..compat import Set 13 | 14 | __all__ = ["APIRequest"] 15 | 16 | # For cases when None can't be used 17 | _DEFAULT_TIMEOUT = object() 18 | 19 | class APIRequest: 20 | """ 21 | Base class for all API requests. 22 | 23 | :param session: an instance of :any:`aiohttp.ClientSession` 24 | :param args: `dict` of arguments, that will be passed to `process_args` 25 | :param timeout: `float` or :any:`aiohttp.ClientTimeout`, request timeout 26 | :param headers: `dict` or `None`, additional request headers 27 | :param n_retries: `int`, maximum number of retries 28 | :param retry_interval: delay between retries in seconds 29 | :param kwargs: other arguments for :any:`aiohttp.ClientSession.request` 30 | 31 | :ivar url: `str`, request URL 32 | :ivar method: `str`, request method 33 | :ivar content_type: `str`, Content-Type header ("application/x-www-form-urlencoded" by default) 34 | :ivar timeout: `float` or :any:`aiohttp.ClientTimeout`, request timeout 35 | :ivar n_retries: `int`, maximum number of retries 36 | :ivar success_codes: `list`-like, list of response codes that indicate request's success 37 | :ivar retry_interval: `float`, delay between retries in seconds 38 | """ 39 | 40 | url: Optional[str] = None 41 | method: Optional[str] = None 42 | content_type: str = "application/x-www-form-urlencoded" 43 | timeout = _DEFAULT_TIMEOUT 44 | n_retries: Optional[int] = None 45 | success_codes: Set[int] = {200} 46 | retry_interval: Optional[Union[int, float]] = None 47 | 48 | response: Optional[aiohttp.ClientResponse] 49 | session: aiohttp.ClientSession 50 | 51 | T = TypeVar("T") 52 | 53 | def __init__(self, session: aiohttp.ClientSession, args: dict, **kwargs): 54 | kwargs = dict(kwargs) 55 | 56 | n_retries = kwargs.pop("n_retries", None) 57 | retry_interval = kwargs.pop("retry_interval", None) 58 | headers = kwargs.pop("headers", {}) 59 | 60 | if headers is None: 61 | headers = {} 62 | 63 | try: 64 | timeout = kwargs["timeout"] 65 | except KeyError: 66 | timeout = self.timeout 67 | 68 | if timeout is _DEFAULT_TIMEOUT: 69 | timeout = settings.DEFAULT_TIMEOUT 70 | 71 | kwargs["timeout"] = timeout 72 | 73 | if n_retries is None: 74 | n_retries = self.n_retries 75 | if n_retries is None: 76 | n_retries = settings.DEFAULT_N_RETRIES 77 | 78 | if retry_interval is None: 79 | retry_interval = self.retry_interval 80 | if retry_interval is None: 81 | retry_interval = settings.DEFAULT_RETRY_INTERVAL 82 | 83 | self.session = session 84 | self.args = args 85 | self.send_kwargs = kwargs 86 | self.timeout = timeout 87 | self.n_retries = n_retries 88 | self.retry_interval = retry_interval 89 | self.headers = headers 90 | self.response = None 91 | self.data = {} 92 | self.params = {} 93 | 94 | self.process_args(**self.args) 95 | 96 | def process_args(self) -> None: 97 | raise NotImplementedError 98 | 99 | async def _attempt(self) -> None: 100 | headers = CaseInsensitiveDict(self.session.headers) 101 | headers["Content-Type"] = self.content_type 102 | headers.update(self.headers) 103 | 104 | kwargs = dict(self.send_kwargs) 105 | kwargs.update({"headers": headers, 106 | "data": self.data, 107 | "params": self.params}) 108 | 109 | assert self.method is not None 110 | assert self.url is not None 111 | 112 | self.response = await self.session.request(self.method, self.url, **kwargs) 113 | 114 | success = self.response.status in self.success_codes 115 | 116 | if not success: 117 | raise await get_exception(self.response) 118 | 119 | async def send(self) -> aiohttp.ClientResponse: 120 | """ 121 | Actually send the request 122 | 123 | :returns: :any:`aiohttp.ClientResponse` (`self.response`) 124 | """ 125 | 126 | await auto_retry(self._attempt, self.n_retries, self.retry_interval) 127 | 128 | assert self.response is not None 129 | 130 | return self.response 131 | 132 | def process_json(self, js: Optional[dict], **kwargs) -> T: 133 | """ 134 | Process the JSON response. 135 | 136 | :param js: `dict`, JSON response 137 | :param kwargs: extra arguments (optional) 138 | 139 | :returns: processed response, can be anything 140 | """ 141 | 142 | raise NotImplementedError 143 | 144 | async def process(self, **kwargs) -> T: 145 | """ 146 | Process the response. 147 | 148 | :returns: depends on `self.process_json()` 149 | """ 150 | 151 | assert self.response is not None 152 | 153 | try: 154 | result = await self.response.json() 155 | except (ValueError, RuntimeError): 156 | result = None 157 | 158 | try: 159 | return self.process_json(result, **kwargs) 160 | except ValueError as e: 161 | raise InvalidResponseError(f"Server returned invalid response: {e}") 162 | -------------------------------------------------------------------------------- /yadisk_async/common.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import datetime 4 | 5 | from .compat import Callable, List, AsyncIterable 6 | 7 | from typing import Optional, TypeVar, Any, Union, IO, Protocol 8 | 9 | __all__ = ["typed_list", "int_or_error", "str_or_error", "bool_or_error", 10 | "dict_or_error", "str_or_dict_or_error", "yandex_date", "is_operation_link", 11 | "is_resource_link", "is_public_resource_link", "ensure_path_has_schema", 12 | "FileOrPath", "FileOrPathDestination", "CaseInsensitiveDict"] 13 | 14 | T = TypeVar("T", bound=Callable) 15 | 16 | def typed_list(datatype: T) -> Callable[[Optional[List]], List[T]]: 17 | def list_factory(iterable: Optional[List] = None) -> List[T]: 18 | if iterable is None: 19 | return [] 20 | 21 | if not isinstance(iterable, list): 22 | raise ValueError(f"Expected a list, got {type(iterable)}") 23 | 24 | return [datatype(i) for i in iterable] 25 | 26 | return list_factory 27 | 28 | def int_or_error(x: Any) -> int: 29 | if not isinstance(x, int): 30 | raise ValueError(f"{repr(x)} is not an integer") 31 | 32 | return x 33 | 34 | def str_or_error(x: Any) -> str: 35 | if not isinstance(x, str): 36 | raise ValueError(f"{repr(x)} is not a string") 37 | 38 | return x 39 | 40 | def bool_or_error(x: Any) -> bool: 41 | if not isinstance(x, bool): 42 | raise ValueError(f"{repr(x)} is not a boolean value") 43 | 44 | return x 45 | 46 | def dict_or_error(x: Any) -> dict: 47 | if not isinstance(x, dict): 48 | raise ValueError(f"{repr(x)} is not a dict") 49 | 50 | return x 51 | 52 | def str_or_dict_or_error(x: Any) -> Union[str, dict]: 53 | if not isinstance(x, (str, dict)): 54 | raise ValueError(f"{repr(x)} is not a string nor a dict") 55 | 56 | return x 57 | 58 | def yandex_date(string): 59 | return datetime.datetime.strptime(string[:-3] + string[-2:], "%Y-%m-%dT%H:%M:%S%z") 60 | 61 | def is_operation_link(link: str) -> bool: 62 | if link.startswith("https://cloud-api.yandex.net/v1/disk/operations/"): 63 | return True 64 | 65 | # Same but http:// version 66 | return link.startswith("http://cloud-api.yandex.net/v1/disk/operations/") 67 | 68 | def is_resource_link(url: str) -> bool: 69 | if url.startswith("https://cloud-api.yandex.net/v1/disk/resources?"): 70 | return True 71 | 72 | # Check also for HTTP version 73 | return url.startswith("http://cloud-api.yandex.net/v1/disk/resources?") 74 | 75 | def is_public_resource_link(url: str) -> bool: 76 | if url.startswith("https://cloud-api.yandex.net/v1/disk/public/resources?"): 77 | return True 78 | 79 | # Check also for HTTP version 80 | return url.startswith("http://cloud-api.yandex.net/v1/disk/public/resources?") 81 | 82 | def ensure_path_has_schema(path: str, default_schema: str = "disk") -> str: 83 | # Modifies path to always have a schema (disk:/, trash:/ or app:/). 84 | # Without the schema Yandex.Disk won't let you upload filenames with the ':' character. 85 | # See https://github.com/ivknv/yadisk/issues/26 for more details 86 | 87 | KNOWN_SCHEMAS = ("disk:", "trash:", "app:") 88 | 89 | if path in KNOWN_SCHEMAS: 90 | return default_schema + ":/" + path 91 | 92 | if path.startswith("/"): 93 | return default_schema + ":" + path 94 | 95 | if any(path.startswith(schema + "/") for schema in KNOWN_SCHEMAS): 96 | return path 97 | 98 | return default_schema + ":/" + path 99 | 100 | class AsyncFileLike(Protocol): 101 | async def read(self, size: int = ..., /) -> Union[str, bytes]: ... 102 | async def write(self, buffer: Any, /) -> int: ... 103 | async def seek(self, pos: int, whence: int = ..., /) -> int: ... 104 | async def tell(self) -> int: ... 105 | 106 | class BinaryAsyncFileLike(Protocol): 107 | async def read(self, size: int = ..., /) -> bytes: ... 108 | async def write(self, buffer: Any, /) -> int: ... 109 | async def seek(self, pos: int, whence: int = ..., /) -> int: ... 110 | async def tell(self) -> int: ... 111 | 112 | FileOrPath = Union[ 113 | str, 114 | bytes, 115 | IO, 116 | AsyncFileLike, 117 | Callable[[], AsyncIterable[bytes]]] 118 | 119 | FileOrPathDestination = Union[ 120 | str, 121 | bytes, 122 | IO[bytes], 123 | BinaryAsyncFileLike] 124 | 125 | # https://stackoverflow.com/a/32888599/3653520 126 | class CaseInsensitiveDict(dict): 127 | K = TypeVar("K") 128 | 129 | @classmethod 130 | def _k(cls, key: K) -> K: 131 | return key.lower() if isinstance(key, str) else key 132 | 133 | def __init__(self, *args, **kwargs): 134 | super().__init__(*args, **kwargs) 135 | self._convert_keys() 136 | 137 | def __getitem__(self, key: K) -> Any: 138 | return super().__getitem__(self.__class__._k(key)) 139 | 140 | def __setitem__(self, key: K, value: Any) -> None: 141 | super().__setitem__(self.__class__._k(key), value) 142 | 143 | def __delitem__(self, key: K) -> Any: 144 | return super().__delitem__(self.__class__._k(key)) 145 | 146 | def __contains__(self, key: K) -> bool: 147 | return super().__contains__(self.__class__._k(key)) 148 | 149 | def pop(self, key: K, /, *args, **kwargs) -> Any: 150 | return super().pop(self.__class__._k(key), *args, **kwargs) 151 | 152 | def get(self, key: K, /, *args, **kwargs) -> Any: 153 | return super().get(self.__class__._k(key), *args, **kwargs) 154 | 155 | def setdefault(self, key: K, *args, **kwargs) -> Any: 156 | return super().setdefault(self.__class__._k(key), *args, **kwargs) 157 | 158 | def update(self, E: dict = {}, **F) -> None: 159 | super().update(self.__class__(E)) 160 | super().update(self.__class__(**F)) 161 | 162 | def _convert_keys(self) -> None: 163 | for k in list(self.keys()): 164 | v = super(CaseInsensitiveDict, self).pop(k) 165 | self.__setitem__(k, v) 166 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # -*- coding: utf-8 -*- 3 | # 4 | # YaDisk documentation build configuration file, created by 5 | # sphinx-quickstart on Sun Oct 1 19:06:55 2017. 6 | # 7 | # This file is execfile()d with the current directory set to its 8 | # containing dir. 9 | # 10 | # Note that not all possible configuration values are present in this 11 | # autogenerated file. 12 | # 13 | # All configuration values have a default; values that are commented out 14 | # serve to show the default. 15 | 16 | # If extensions (or modules to document with autodoc) are in another directory, 17 | # add these directories to sys.path here. If the directory is relative to the 18 | # documentation root, use os.path.abspath to make it absolute, like shown here. 19 | # 20 | import os 21 | import sys 22 | sys.path.insert(0, os.path.abspath('..')) 23 | 24 | # -- General configuration ------------------------------------------------ 25 | 26 | # If your documentation needs a minimal Sphinx version, state it here. 27 | # 28 | # needs_sphinx = '1.0' 29 | 30 | # Add any Sphinx extension module names here, as strings. They can be 31 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom 32 | # ones. 33 | extensions = ['sphinx.ext.autodoc', 34 | 'sphinx.ext.coverage', 35 | 'sphinx.ext.viewcode', 36 | 'sphinx.ext.intersphinx'] 37 | 38 | intersphinx_mapping = {"python": ("https://docs.python.org/3", None), 39 | "aiohttp": ("https://aiohttp.readthedocs.io/en/stable/", None)} 40 | 41 | # Add any paths that contain templates here, relative to this directory. 42 | templates_path = ['_templates'] 43 | 44 | # The suffix(es) of source filenames. 45 | # You can specify multiple suffix as a list of string: 46 | # 47 | # source_suffix = ['.rst', '.md'] 48 | source_suffix = '.rst' 49 | 50 | # The master toctree document. 51 | master_doc = 'index' 52 | 53 | # General information about the project. 54 | project = 'YaDisk-async' 55 | copyright = '2023, Ivan Konovalov' 56 | author = 'Ivan Konovalov' 57 | 58 | # The version info for the project you're documenting, acts as replacement for 59 | # |version| and |release|, also used in various other places throughout the 60 | # built documents. 61 | # 62 | # The short X.Y version. 63 | version = '1.4.5' 64 | # The full version, including alpha/beta/rc tags. 65 | release = '1.4.5' 66 | 67 | # The language for content autogenerated by Sphinx. Refer to documentation 68 | # for a list of supported languages. 69 | # 70 | # This is also used if you do content translation via gettext catalogs. 71 | # Usually you set "language" from the command line for these cases. 72 | language = 'en' 73 | 74 | # List of patterns, relative to source directory, that match files and 75 | # directories to ignore when looking for source files. 76 | # This patterns also effect to html_static_path and html_extra_path 77 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 78 | 79 | # The name of the Pygments (syntax highlighting) style to use. 80 | pygments_style = 'sphinx' 81 | 82 | # If true, `todo` and `todoList` produce output, else they produce nothing. 83 | todo_include_todos = False 84 | 85 | 86 | # -- Options for HTML output ---------------------------------------------- 87 | 88 | # The theme to use for HTML and HTML Help pages. See the documentation for 89 | # a list of builtin themes. 90 | # 91 | html_theme = 'sphinx_rtd_theme' 92 | 93 | # Theme options are theme-specific and customize the look and feel of a theme 94 | # further. For a list of options available for each theme, see the 95 | # documentation. 96 | # 97 | # html_theme_options = {} 98 | 99 | # Add any paths that contain custom static files (such as style sheets) here, 100 | # relative to this directory. They are copied after the builtin static files, 101 | # so a file named "default.css" will overwrite the builtin "default.css". 102 | html_static_path = ['_static'] 103 | 104 | # Custom sidebar templates, must be a dictionary that maps document names 105 | # to template names. 106 | # 107 | # This is required for the alabaster theme 108 | # refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars 109 | html_sidebars = { 110 | '**': [ 111 | 'about.html', 112 | 'navigation.html', 113 | 'relations.html', # needs 'show_related': True theme option to display 114 | 'searchbox.html', 115 | 'donate.html', 116 | ] 117 | } 118 | 119 | 120 | # -- Options for HTMLHelp output ------------------------------------------ 121 | 122 | # Output file base name for HTML help builder. 123 | htmlhelp_basename = 'YaDiskdoc' 124 | 125 | 126 | # -- Options for LaTeX output --------------------------------------------- 127 | 128 | latex_elements = { 129 | # The paper size ('letterpaper' or 'a4paper'). 130 | # 131 | # 'papersize': 'letterpaper', 132 | 133 | # The font size ('10pt', '11pt' or '12pt'). 134 | # 135 | # 'pointsize': '10pt', 136 | 137 | # Additional stuff for the LaTeX preamble. 138 | # 139 | # 'preamble': '', 140 | 141 | # Latex figure (float) alignment 142 | # 143 | # 'figure_align': 'htbp', 144 | } 145 | 146 | # Grouping the document tree into LaTeX files. List of tuples 147 | # (source start file, target name, title, 148 | # author, documentclass [howto, manual, or own class]). 149 | latex_documents = [ 150 | (master_doc, 'YaDisk.tex', 'YaDisk Documentation', 151 | 'Ivan Konovalov', 'manual'), 152 | ] 153 | 154 | 155 | # -- Options for manual page output --------------------------------------- 156 | 157 | # One entry per manual page. List of tuples 158 | # (source start file, name, description, authors, manual section). 159 | man_pages = [ 160 | (master_doc, 'yadisk', 'YaDisk Documentation', 161 | [author], 1) 162 | ] 163 | 164 | 165 | # -- Options for Texinfo output ------------------------------------------- 166 | 167 | # Grouping the document tree into Texinfo files. List of tuples 168 | # (source start file, target name, title, author, 169 | # dir menu entry, description, category) 170 | texinfo_documents = [ 171 | (master_doc, 'YaDisk', 'YaDisk Documentation', 172 | author, 'YaDisk', 'One line description of project.', 173 | 'Miscellaneous'), 174 | ] 175 | 176 | 177 | 178 | -------------------------------------------------------------------------------- /yadisk_async/objects/disk.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | from functools import partial 4 | from .yadisk_object import YaDiskObject 5 | from ..common import str_or_error, bool_or_error, int_or_error 6 | 7 | from typing import Optional, NoReturn, TYPE_CHECKING 8 | 9 | if TYPE_CHECKING: 10 | from ..yadisk import YaDisk 11 | 12 | __all__ = ["DiskInfoObject", "SystemFoldersObject", "UserObject", "UserPublicInfoObject"] 13 | 14 | class DiskInfoObject(YaDiskObject): 15 | """ 16 | Disk information object. 17 | 18 | :param disk_info: `dict` or `None` 19 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 20 | 21 | :ivar max_file_size: `int`, maximum supported file size (bytes) 22 | :ivar paid_max_file_size: `int`, maximum supported file size for a paid account (bytes) 23 | :ivar unlimited_autoupload_enabled: `bool`, tells whether unlimited 24 | autoupload from mobile devices is enabled 25 | :ivar total_space: `int`, total disk size (bytes) 26 | :ivar trash_size: `int`, amount of space used by trash (bytes), part of `used_space` 27 | :ivar is_paid: `bool`, tells if the account is paid or not 28 | :ivar used_space: `int`, amount of space used (bytes) 29 | :ivar system_folders: :any:`SystemFoldersObject`, paths to the system folders 30 | :ivar user: :any:`UserObject`, owner of the disk 31 | :ivar revision: `int`, current revision of Yandex.Disk 32 | """ 33 | 34 | max_file_size: Optional[int] 35 | paid_max_file_size: Optional[int] 36 | unlimited_autoupload_enabled: Optional[bool] 37 | total_space: Optional[int] 38 | trash_size: Optional[int] 39 | is_paid: Optional[bool] 40 | used_space: Optional[int] 41 | system_folders: "SystemFoldersObject" 42 | user: "UserObject" 43 | revision: Optional[int] 44 | 45 | def __init__(self, disk_info: Optional[dict] = None, yadisk: Optional["YaDisk"] = None): 46 | YaDiskObject.__init__( 47 | self, 48 | {"max_file_size": int_or_error, 49 | "paid_max_file_size": int_or_error, 50 | "unlimited_autoupload_enabled": bool_or_error, 51 | "total_space": int_or_error, 52 | "trash_size": int_or_error, 53 | "is_paid": bool_or_error, 54 | "used_space": int_or_error, 55 | "system_folders": partial(SystemFoldersObject, yadisk=yadisk), 56 | "user": partial(UserObject, yadisk=yadisk), 57 | "revision": int_or_error}, 58 | yadisk) 59 | 60 | self.import_fields(disk_info) 61 | 62 | class SystemFoldersObject(YaDiskObject): 63 | """ 64 | Object, containing paths to system folders. 65 | 66 | :param system_folders: `dict` or `None` 67 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 68 | 69 | :ivar odnoklassniki: `str`, path to the Odnoklassniki folder 70 | :ivar google: `str`, path to the Google+ folder 71 | :ivar instagram: `str`, path to the Instagram folder 72 | :ivar vkontakte: `str`, path to the VKontakte folder 73 | :ivar attach: `str`, path to the mail attachments folder 74 | :ivar mailru: `str`, path to the My World folder 75 | :ivar downloads: `str`, path to the Downloads folder 76 | :ivar applications: `str` path to the Applications folder 77 | :ivar facebook: `str`, path to the Facebook folder 78 | :ivar social: `str`, path to the social networks folder 79 | :ivar messenger: `str`, path to the Messenger Files folder 80 | :ivar calendar: `str`, path to the Meeting Materials folder 81 | :ivar photostream: `str`, path to the camera folder 82 | :ivar screenshots: `str`, path to the screenshot folder 83 | :ivar scans: `str`, path to the Scans folder 84 | """ 85 | 86 | odnoklassniki: Optional[str] 87 | google: Optional[str] 88 | instagram: Optional[str] 89 | vkontakte: Optional[str] 90 | attach: Optional[str] 91 | mailru: Optional[str] 92 | downloads: Optional[str] 93 | applications: Optional[str] 94 | facebook: Optional[str] 95 | social: Optional[str] 96 | messenger: Optional[str] 97 | calendar: Optional[str] 98 | photostream: Optional[str] 99 | screenshots: Optional[str] 100 | scans: Optional[str] 101 | 102 | def __init__(self, 103 | system_folders: Optional[dict] = None, 104 | yadisk: Optional["YaDisk"] = None): 105 | YaDiskObject.__init__( 106 | self, 107 | {"odnoklassniki": str_or_error, 108 | "google": str_or_error, 109 | "instagram": str_or_error, 110 | "vkontakte": str_or_error, 111 | "attach": str_or_error, 112 | "mailru": str_or_error, 113 | "downloads": str_or_error, 114 | "applications": str_or_error, 115 | "facebook": str_or_error, 116 | "social": str_or_error, 117 | "messenger": str_or_error, 118 | "calendar": str_or_error, 119 | "photostream": str_or_error, 120 | "screenshots": str_or_error, 121 | "scans": str_or_error}, 122 | yadisk) 123 | 124 | self.import_fields(system_folders) 125 | 126 | class UserObject(YaDiskObject): 127 | """ 128 | User object. 129 | 130 | :param user: `dict` or `None` 131 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 132 | 133 | :ivar country: `str`, user's country 134 | :ivar login: `str`, user's login 135 | :ivar display_name: `str`, user's display name 136 | :ivar uid: `str`, user's UID 137 | """ 138 | 139 | country: Optional[str] 140 | login: Optional[str] 141 | display_name: Optional[str] 142 | uid: Optional[str] 143 | 144 | def __init__(self, user: Optional[dict] = None, yadisk: Optional["YaDisk"] = None): 145 | YaDiskObject.__init__( 146 | self, 147 | {"country": str_or_error, 148 | "login": str_or_error, 149 | "display_name": str_or_error, 150 | "uid": str_or_error}, 151 | yadisk) 152 | 153 | self.import_fields(user) 154 | 155 | class UserPublicInfoObject(UserObject): 156 | """ 157 | Public user information object. 158 | Inherits from :any:`UserObject` for compatibility. 159 | 160 | :param public_user_info: `dict` or `None` 161 | :param yadisk: :any:`YaDisk` or `None`, `YaDisk` object 162 | 163 | :ivar login: `str`, user's login 164 | :ivar display_name: `str`, user's display name 165 | :ivar uid: `str`, user's UID 166 | """ 167 | 168 | country: NoReturn 169 | 170 | def __init__(self, 171 | public_user_info: Optional[dict] = None, 172 | yadisk: Optional["YaDisk"] = None): 173 | UserObject.__init__(self, None, yadisk) 174 | self.remove_field("country") 175 | self.import_fields(public_user_info) 176 | -------------------------------------------------------------------------------- /COPYING.lesser: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /docs/intro.rst: -------------------------------------------------------------------------------- 1 | Introduction 2 | ============ 3 | 4 | .. _YaDisk: https://github.com/ivknv/yadisk 5 | 6 | YaDisk-async is a modified version of `YaDisk`_ with :code:`async/await` support. 7 | It uses `aiohttp` instead of `requests`. 8 | 9 | .. warning:: 10 | This library is no longer maintained, use `yadisk `__>=2.0.0 instead, it also 11 | supports async API. 12 | 13 | The usage is more or less the same, except that you have to manually close 14 | all the sessions (can be done with :any:`YaDisk.close` or through :code:`async with` statement). 15 | 16 | Installation 17 | ************ 18 | 19 | .. code:: bash 20 | 21 | pip install yadisk-async 22 | 23 | or 24 | 25 | .. code:: bash 26 | 27 | python setup.py install 28 | 29 | Examples 30 | ******** 31 | 32 | .. code:: python 33 | 34 | import yadisk_async 35 | 36 | y = yadisk_async.YaDisk(token="") 37 | # or 38 | # y = yadisk_async.YaDisk("", "", "") 39 | 40 | # Check if the token is valid 41 | print(await y.check_token()) 42 | 43 | # Get disk information 44 | print(await y.get_disk_info()) 45 | 46 | # Print files and directories at "/some/path" 47 | print([i async for i in await y.listdir("/some/path")]) 48 | 49 | # Upload "file_to_upload.txt" to "/destination.txt" 50 | await y.upload("file_to_upload.txt", "/destination.txt") 51 | 52 | # Same thing 53 | with open("file_to_upload.txt", "rb") as f: 54 | await y.upload(f, "/destination.txt") 55 | 56 | # Same but with aiofiles 57 | async with aiofiles.open("file_to_upload.txt", "rb") as f: 58 | await y.upload(f, "/destination.txt") 59 | 60 | # Download "/some-file-to-download.txt" to "downloaded.txt" 61 | await y.download("/some-file-to-download.txt", "downloaded.txt") 62 | 63 | # Same but with aiofiles 64 | async with aiofiles.open("downloaded.txt", "wb") as f: 65 | await y.download("/some-file-to-download.txt", f) 66 | 67 | # Permanently remove "/file-to-remove" 68 | await y.remove("/file-to-remove", permanently=True) 69 | 70 | # Create a new directory at "/test-dir" 71 | print(await y.mkdir("/test-dir")) 72 | 73 | # Always remember to close all the connections or you'll get a warning 74 | await y.close() 75 | 76 | Receiving token with confirmation code 77 | ###################################### 78 | 79 | .. code:: python 80 | 81 | import asyncio 82 | import sys 83 | import yadisk_async 84 | 85 | async def main(): 86 | async with yadisk_async.YaDisk("application-id>", "") as y: 87 | url = y.get_code_url() 88 | 89 | print("Go to the following url: %s" % url) 90 | code = input("Enter the confirmation code: ") 91 | 92 | try: 93 | response = await y.get_token(code) 94 | except yadisk_async.exceptions.BadRequestError: 95 | print("Bad code") 96 | sys.exit(1) 97 | 98 | y.token = response.access_token 99 | 100 | if await y.check_token(): 101 | print("Sucessfully received token!") 102 | else: 103 | print("Something went wrong. Not sure how though...") 104 | 105 | loop = asyncio.get_event_loop() 106 | loop.run_until_complete(main()) 107 | 108 | Recursive upload 109 | ################ 110 | 111 | .. code:: python 112 | 113 | import asyncio 114 | import posixpath 115 | import os 116 | import yadisk_async 117 | 118 | def recursive_upload(from_dir, to_dir, n_parallel_requests=5): 119 | loop = asyncio.get_event_loop() 120 | 121 | y = yadisk_async.YaDisk(token="") 122 | 123 | try: 124 | async def upload_files(queue): 125 | while queue: 126 | in_path, out_path = queue.pop(0) 127 | 128 | print("Uploading %s -> %s" % (in_path, out_path)) 129 | 130 | try: 131 | await y.upload(in_path, out_path) 132 | except yadisk_async.exceptions.PathExistsError: 133 | print("%s already exists" % (out_path,)) 134 | 135 | async def create_dirs(queue): 136 | while queue: 137 | path = queue.pop(0) 138 | 139 | print("Creating directory %s" % (path,)) 140 | 141 | try: 142 | await y.mkdir(path) 143 | except yadisk_async.exceptions.PathExistsError: 144 | print("%s already exists" % (path,)) 145 | 146 | mkdir_queue = [] 147 | upload_queue = [] 148 | 149 | print("Creating directory %s" % (to_dir,)) 150 | 151 | try: 152 | loop.run_until_complete(y.mkdir(to_dir)) 153 | except yadisk_async.exceptions.PathExistsError: 154 | print("%s already exists" % (to_dir,)) 155 | 156 | for root, dirs, files in os.walk(from_dir): 157 | rel_dir_path = root.split(from_dir)[1].strip(os.path.sep) 158 | rel_dir_path = rel_dir_path.replace(os.path.sep, "/") 159 | dir_path = posixpath.join(to_dir, rel_dir_path) 160 | 161 | for dirname in dirs: 162 | mkdir_queue.append(posixpath.join(dir_path, dirname)) 163 | 164 | for filename in files: 165 | out_path = posixpath.join(dir_path, filename) 166 | rel_dir_path_sys = rel_dir_path.replace("/", os.path.sep) 167 | in_path = os.path.join(from_dir, rel_dir_path_sys, filename) 168 | 169 | upload_queue.append((in_path, out_path)) 170 | 171 | tasks = [upload_files(upload_queue) for i in range(n_parallel_requests)] 172 | tasks.extend(create_dirs(mkdir_queue) for i in range(n_parallel_requests)) 173 | 174 | loop.run_until_complete(asyncio.gather(*tasks)) 175 | finally: 176 | loop.run_until_complete(y.close()) 177 | 178 | from_dir = input("Directory to upload: ") 179 | to_dir = input("Destination directory: ") 180 | 181 | recursive_upload(from_dir, to_dir, 5) 182 | 183 | Setting custom properties of files 184 | ################################## 185 | 186 | .. code:: python 187 | 188 | import asyncio 189 | import yadisk_async 190 | 191 | async def main(): 192 | async with yadisk_async.YaDisk(token="") as y: 193 | path = input("Enter a path to patch: ") 194 | properties = {"speed_of_light": 299792458, 195 | "speed_of_light_units": "meters per second", 196 | "message_for_owner": "MWAHAHA! Your file has been patched by an evil script!"} 197 | 198 | meta = await y.patch(path, properties) 199 | print("\nNew properties: ") 200 | 201 | for k, v in meta.custom_properties.items(): 202 | print("%s: %r" % (k, v)) 203 | 204 | answer = input("\nWant to get rid of them? (y/[n]) ") 205 | 206 | if answer.lower() in ("y", "yes"): 207 | properties = {k: None for k in properties} 208 | await y.patch(path, properties) 209 | print("Everything's back as usual") 210 | 211 | loop = asyncio.get_event_loop() 212 | loop.run_until_complete(main()) 213 | 214 | Emptying the trash bin 215 | ###################### 216 | 217 | .. code:: python 218 | 219 | import asyncio 220 | import sys 221 | import yadisk_async 222 | 223 | async def main(): 224 | async with yadisk_async.YaDisk(token="") as y: 225 | answer = input("Are you sure about this? (y/[n]) ") 226 | 227 | if answer.lower() in ("y", "yes"): 228 | print("Emptying the trash bin...") 229 | operation = await y.remove_trash("/") 230 | print("It might take a while...") 231 | 232 | if operation is None: 233 | print("Nevermind. The deed is done.") 234 | sys.exit(0) 235 | 236 | while True: 237 | status = await y.get_operation_status(operation.href) 238 | 239 | if status == "in-progress": 240 | await asyncio.sleep(5) 241 | print("Still waiting...") 242 | elif status == "success": 243 | print("Success!") 244 | break 245 | else: 246 | print("Got some weird status: %r" % (status,)) 247 | print("That's not normal") 248 | break 249 | else: 250 | print("Not going to do anything") 251 | 252 | loop = asyncio.get_event_loop() 253 | loop.run_until_complete(main()) 254 | -------------------------------------------------------------------------------- /docs/changelog.rst: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | .. _yadisk: https://github.com/ivknv/yadisk 5 | 6 | .. _issue #2: https://github.com/ivknv/yadisk/issues/2 7 | .. _issue #4: https://github.com/ivknv/yadisk/issues/4 8 | .. _issue #7: https://github.com/ivknv/yadisk/issues/7 9 | .. _PR #1: https://github.com/ivknv/yadisk-async/pull/1 10 | .. _issue #23: https://github.com/ivknv/yadisk/issues/23 11 | .. _PR #6: https://github.com/ivknv/yadisk-async/pull/6 12 | .. _issue #26: https://github.com/ivknv/yadisk/issues/26 13 | .. _issue #28: https://github.com/ivknv/yadisk/issues/28 14 | .. _issue #29: https://github.com/ivknv/yadisk/issues/29 15 | .. _PR #31: https://github.com/ivknv/yadisk/pull/31 16 | 17 | * **Release 1.4.5 (2023-12-14)** 18 | 19 | * Added a deprecation warning on import 20 | * This library is no longer maintained, use 21 | `yadisk `__>=2.0.0 instead, it also 22 | supports async API 23 | 24 | * **Release 1.4.4 (2023-10-15)** 25 | 26 | * `upload()` and `download()` (and related) methods can now 27 | upload/download non-seekable file-like objects (e.g. `stdin` or `stdout` 28 | when open in binary mode), see `PR #31`_ 29 | 30 | * **Release 1.4.3 (2023-04-22)** 31 | 32 | * `app:/` paths now work correctly (see `issue #26`_) 33 | 34 | * **Release 1.4.2 (2023-03-20)** 35 | 36 | * Fixed `issue #29`_: TypeError: 'type' object is not subscriptable 37 | * Fixed a bug affecting Python <3.9: TypeError: Too many parameters for typing.AsyncIterable; actual 2, expected 1 38 | 39 | * **Release 1.4.1 (2023-02-28)** 40 | 41 | * Fixed `issue #28`_: calling `download_public()` with `path` keyword argument raises `TypeError` 42 | * Fixed `AttributeError` raised when calling `ResourceLinkObject.public_listdir()` 43 | 44 | * **Release 1.4.0 (2023-01-30)** 45 | 46 | * Added convenience methods to `...Object` objects (e.g. see `ResourceObject`) 47 | * Added type hints 48 | * Improved error checking and response validation 49 | * Added `InvalidResponseError`, `PayloadTooLargeError`, `UploadTrafficLimitExceededError` 50 | * Added a few missing fields to `DiskInfoObject` and `SystemFoldersObject` 51 | * Added `rename()`, `upload_by_link()` and `download_by_link()` methods 52 | * Added `default_args` field for `YaDisk` object 53 | * `download()` and `upload()` now return `ResourceLinkObject` 54 | * Returned `LinkObject` instances have been replaced by more specific subclasses 55 | * :any:`TimeoutError` now also triggers a retry 56 | * Added support for async files for `download()` and `upload()` 57 | * Use `aiofiles` to open files by default 58 | 59 | * **Release 1.3.6 (2023-01-20)** 60 | 61 | * Fixed `issue #26`_: ':' character in filenames causes `BadRequestError`. 62 | This is due the behavior of Yandex.Disk's REST API itself but is avoided 63 | on the library level with this fix. 64 | 65 | * **Release 1.3.5 (2022-11-10)** 66 | 67 | * Fixed a bug in `is_file()` and `is_dir()`: a typo was causing 68 | `AttributeError` (`PR #6`_). 69 | 70 | * **Release 1.3.4 (2022-08-17)** 71 | 72 | * Fixed a bug in `check_token()`: could throw `ForbiddenError` if 73 | the application lacks necessary permissions (`issue #23`_). 74 | 75 | * **Release 1.3.3 (2021-12-31)** 76 | 77 | * Fixed an issue where `http://` links were not recognized as operation links 78 | (they were assumed to always be `https://`, since all the other 79 | requests are always HTTPS). 80 | Occasionally, Yandex.Disk can for some reason return an `http://` link 81 | to an asynchronous operation instead of `https://`. 82 | Both links are now recognized correctly and an `https://` version will 83 | always be used by `get_operation_status()`, regardless of which one 84 | Yandex.Disk returned. 85 | 86 | * **Release 1.3.2 (2021-07-10)** 87 | 88 | * Fixed `__version__` having the wrong value 89 | 90 | * **Release 1.3.1 (2021-07-10)** 91 | 92 | * Fixed `AttributeError` in `SessionWithHeaders` (`PR #1`_) 93 | * Fixed trash bin tests 94 | 95 | * **Release 1.3.0 (2019-07-06)** 96 | 97 | * Modified the original library (`yadisk`_) to support `async/await` 98 | * The library was renamed to `yadisk-async` 99 | 100 | The following releases are for `yadisk`_, the original library: 101 | 102 | * **Release 1.2.14 (2019-03-26)** 103 | 104 | * Fixed a `TypeError` in `get_public_*` functions when passing `path` parameter 105 | (see `issue #7`_) 106 | * Added `unlimited_autoupload_enabled` attribute for `DiskInfoObject` 107 | 108 | * **Release 1.2.13 (2019-02-23)** 109 | 110 | * Added `md5` parameter for `remove()` 111 | * Added `UserPublicInfoObject` 112 | * Added `country` attribute for `UserObject` 113 | * Added `photoslice_time` attribute for `ResourceObject`, `PublicResourceObject` 114 | and `TrashResourceObject` 115 | 116 | * **Release 1.2.12 (2018-10-11)** 117 | 118 | * Fixed `fields` parameter not working properly in `listdir()` (`issue #4`_) 119 | 120 | * **Release 1.2.11 (2018-06-30)** 121 | 122 | * Added the missing parameter `sort` for `get_meta()` 123 | * Added `file` and `antivirus_status` attributes for `ResourceObject`, 124 | `PublicResourceObject` and `TrashResourceObject` 125 | * Added `headers` parameter 126 | * Fixed a typo in `download()` and `download_public()` (`issue #2`_) 127 | * Removed `*args` parameter everywhere 128 | 129 | * **Release 1.2.10 (2018-06-14)** 130 | 131 | * Fixed `timeout=None` behavior. `None` is supposed to mean 'no timeout' but 132 | in the older versions it was synonymous with the default timeout. 133 | 134 | * **Release 1.2.9 (2018-04-28)** 135 | 136 | * Changed the license to LGPLv3 (see `COPYING` and `COPYING.lesser`) 137 | * Other package info updates 138 | 139 | * **Release 1.2.8 (2018-04-17)** 140 | 141 | * Fixed a couple of typos: `PublicResourceListObject.items` and 142 | `TrashResourceListObject.items` had wrong types 143 | * Substitute field aliases in `fields` parameter when performing 144 | API requests (e.g. `embedded` -> `_embedded`) 145 | 146 | * **Release 1.2.7 (2018-04-15)** 147 | 148 | * Fixed a file rewinding bug when uploading/downloading files after a retry 149 | 150 | * **Release 1.2.6 (2018-04-13)** 151 | 152 | * Now caching `requests` sessions so that open connections 153 | can be reused (which can significantly speed things up sometimes) 154 | * Disable `keep-alive` when uploading/downloading files by default 155 | 156 | * **Release 1.2.5 (2018-03-31)** 157 | 158 | * Fixed an off-by-one bug in `utils.auto_retry()` 159 | (which could sometimes result in `AttributeError`) 160 | * Retry the whole request for `upload()`, `download()` and `download_public()` 161 | * Set `stream=True` for `download()` and `download_public()` 162 | * Other minor fixes 163 | 164 | * **Release 1.2.4 (2018-02-19)** 165 | 166 | * Fixed `TokenObject` having `exprires_in` instead of `expires_in` (fixed a typo) 167 | 168 | * **Release 1.2.3 (2018-01-20)** 169 | 170 | * Fixed a `TypeError` when `WrongResourceTypeError` is raised 171 | 172 | * **Release 1.2.2 (2018-01-19)** 173 | 174 | * `refresh_token()` no longer requires a valid or empty token. 175 | 176 | * **Release 1.2.1 (2018-01-14)** 177 | 178 | * Fixed auto retries not working. Whoops. 179 | 180 | * **Release 1.2.0 (2018-01-14)** 181 | 182 | * Fixed passing `n_retries=0` to `upload()`, 183 | `download()` and `download_public()` 184 | * `upload()`, `download()` and `download_public()` 185 | no longer return anything (see the docs) 186 | * Added `utils` module (see the docs) 187 | * Added `RetriableYaDiskError`, `WrongResourceTypeError`, 188 | `BadGatewayError` and `GatewayTimeoutError` 189 | * `listdir()` now raises `WrongResourceTypeError` 190 | instead of `NotADirectoryError` 191 | 192 | * **Release 1.1.1 (2017-12-29)** 193 | 194 | * Fixed argument handling in `upload()`, `download()` and `download_public()`. 195 | Previously, passing `n_retries` and `retry_interval` would raise an exception (`TypeError`). 196 | 197 | * **Release 1.1.0 (2017-12-27)** 198 | 199 | * Better exceptions (see the docs) 200 | * Added support for `force_async` parameter 201 | * Minor bug fixes 202 | 203 | * **Release 1.0.8 (2017-11-29)** 204 | 205 | * Fixed yet another `listdir()` bug 206 | 207 | * **Release 1.0.7 (2017-11-04)** 208 | 209 | * Added `install_requires` argument to `setup.py` 210 | 211 | * **Release 1.0.6 (2017-11-04)** 212 | 213 | * Return `OperationLinkObject` in some functions 214 | 215 | * **Release 1.0.5 (2017-10-29)** 216 | 217 | * Fixed `setup.py` to exclude tests 218 | 219 | * **Release 1.0.4 (2017-10-23)** 220 | 221 | * Fixed bugs in `upload`, `download` and `listdir` functions 222 | * Set default `listdir` `limit` to `10000` 223 | 224 | * **Release 1.0.3 (2017-10-22)** 225 | 226 | * Added settings 227 | 228 | * **Release 1.0.2 (2017-10-19)** 229 | 230 | * Fixed `get_code_url` function (added missing parameters) 231 | 232 | * **Release 1.0.1 (2017-10-18)** 233 | 234 | * Fixed a major bug in `GetTokenRequest` (added missing parameter) 235 | 236 | * **Release 1.0.0 (2017-10-18)** 237 | 238 | * Initial release 239 | -------------------------------------------------------------------------------- /README.en.rst: -------------------------------------------------------------------------------- 1 | YaDisk-async 2 | ============ 3 | 4 | .. image:: https://img.shields.io/readthedocs/yadisk-async.svg 5 | :alt: Read the Docs 6 | :target: https://yadisk-async.readthedocs.io/en/latest/ 7 | 8 | .. image:: https://img.shields.io/pypi/v/yadisk-async.svg 9 | :alt: PyPI 10 | :target: https://pypi.org/project/yadisk-async 11 | 12 | YaDisk-async is a Yandex.Disk REST API client library with async/await support. 13 | 14 | 15 | This library is no longer maintained, use 16 | `yadisk `__>=2.0.0 instead, it also 17 | supports async API. 18 | 19 | .. _Read the Docs (EN): http://yadisk-async.readthedocs.io 20 | .. _Read the Docs (RU): http://yadisk-async.readthedocs.io/ru/latest 21 | 22 | Documentation is available at `Read the Docs (EN)`_ and `Read the Docs (RU)`_. 23 | 24 | Installation 25 | ************ 26 | 27 | .. code:: bash 28 | 29 | pip install yadisk-async 30 | 31 | or 32 | 33 | .. code:: bash 34 | 35 | python setup.py install 36 | 37 | Examples 38 | ******** 39 | 40 | .. code:: python 41 | 42 | import yadisk_async 43 | 44 | y = yadisk_async.YaDisk(token="") 45 | # or 46 | # y = yadisk_async.YaDisk("", "", "") 47 | 48 | # Check if the token is valid 49 | print(await y.check_token()) 50 | 51 | # Get disk information 52 | print(await y.get_disk_info()) 53 | 54 | # Print files and directories at "/some/path" 55 | print([i async for i in await y.listdir("/some/path")]) 56 | 57 | # Upload "file_to_upload.txt" to "/destination.txt" 58 | await y.upload("file_to_upload.txt", "/destination.txt") 59 | 60 | # Same thing 61 | async with aiofiles.open("file_to_upload.txt", "rb") as f: 62 | await y.upload(f, "/destination.txt") 63 | 64 | # Same thing but with regular files 65 | with open("file_to_upload.txt", "rb") as f: 66 | await y.upload(f, "/destination.txt") 67 | 68 | # Download "/some-file-to-download.txt" to "downloaded.txt" 69 | await y.download("/some-file-to-download.txt", "downloaded.txt") 70 | 71 | # Same thing 72 | async with aiofiles.open("downloaded.txt", "wb") as f: 73 | await y.download("/some-file-to-download.txt", f) 74 | 75 | # Permanently remove "/file-to-remove" 76 | await y.remove("/file-to-remove", permanently=True) 77 | 78 | # Create a new directory at "/test-dir" 79 | print(await y.mkdir("/test-dir")) 80 | 81 | # Always remember to close all the connections or you'll get a warning 82 | await y.close() 83 | 84 | Changelog 85 | ********* 86 | 87 | .. _yadisk: https://github.com/ivknv/yadisk 88 | 89 | .. _issue #2: https://github.com/ivknv/yadisk/issues/2 90 | .. _issue #4: https://github.com/ivknv/yadisk/issues/4 91 | .. _issue #7: https://github.com/ivknv/yadisk/issues/7 92 | .. _PR #1: https://github.com/ivknv/yadisk-async/pull/1 93 | .. _issue #23: https://github.com/ivknv/yadisk/issues/23 94 | .. _PR #6: https://github.com/ivknv/yadisk-async/pull/6 95 | .. _issue #26: https://github.com/ivknv/yadisk/issues/26 96 | .. _issue #28: https://github.com/ivknv/yadisk/issues/28 97 | .. _issue #29: https://github.com/ivknv/yadisk/issues/29 98 | .. _PR #31: https://github.com/ivknv/yadisk/pull/31 99 | 100 | * **Release 1.4.5 (2023-12-14)** 101 | 102 | * Added a deprecation warning on import 103 | * This library is no longer maintained, use 104 | `yadisk `__>=2.0.0 instead, it also 105 | supports async API. 106 | 107 | * **Release 1.4.4 (2023-10-15)** 108 | 109 | * :code:`upload()` and :code:`download()` (and related) methods can now 110 | upload/download non-seekable file-like objects (e.g. :code:`stdin` or :code:`stdout` 111 | when open in binary mode), see `PR #31`_ 112 | 113 | * **Release 1.4.3 (2023-04-22)** 114 | 115 | * :code:`app:/` paths now work correctly (see `issue #26`_) 116 | 117 | * **Release 1.4.2 (2023-03-20)** 118 | 119 | * Fixed `issue #29`_: TypeError: 'type' object is not subscriptable 120 | * Fixed a bug affecting Python <3.9: TypeError: Too many parameters for typing.AsyncIterable; actual 2, expected 1 121 | 122 | * **Release 1.4.1 (2023-02-28)** 123 | 124 | * Fixed `issue #28`_: calling :code:`download_public()` with :code:`path` keyword argument raises :code:`TypeError` 125 | * Fixed :code:`AttributeError` raised when calling :code:`ResourceLinkObject.public_listdir()` 126 | 127 | * **Release 1.4.0 (2023-01-30)** 128 | 129 | * Added convenience methods to :code:`...Object` objects (e.g. see :code:`ResourceObject` in docs) 130 | * Added type hints 131 | * Improved error checking and response validation 132 | * Added :code:`InvalidResponseError`, :code:`PayloadTooLargeError`, :code:`UploadTrafficLimitExceededError` 133 | * Added a few missing fields to :code:`DiskInfoObject` and :code:`SystemFoldersObject` 134 | * Added :code:`rename()`, :code:`upload_by_link()` and :code:`download_by_link()` methods 135 | * Added :code:`default_args` field for :code:`YaDisk` object 136 | * :code:`download()` and :code:`upload()` now return :code:`ResourceLinkObject` 137 | * Returned :code:`LinkObject` instances have been replaced by more specific subclasses 138 | * :code:`TimeoutError` now also triggers a retry 139 | * Added support for async files for :code:`download()` and :code:`upload()` 140 | * Use :code:`aiofiles` to open files by default 141 | 142 | * **Release 1.3.6 (2023-01-20)** 143 | 144 | * Fixed `issue #26`_: ':' character in filenames causes :code:`BadRequestError`. 145 | This is due the behavior of Yandex.Disk's REST API itself but is avoided 146 | on the library level with this fix. 147 | 148 | * **Release 1.3.5 (2022-11-10)** 149 | 150 | * Fixed a bug in :code:`is_file()` and :code:`is_dir()`: a typo was causing 151 | :code:`AttributeError` (`PR #6`_). 152 | 153 | * **Release 1.3.4 (2022-08-17)** 154 | 155 | * Fixed a bug in :code:`check_token()`: could throw :code:`ForbiddenError` if 156 | the application lacks necessary permissions (`issue #23`_). 157 | 158 | * **Release 1.3.3 (2021-12-31)** 159 | 160 | * Fixed an issue where :code:`http://` links were not recognized as operation links 161 | (they were assumed to always be :code:`https://`, since all the other 162 | requests are always HTTPS). 163 | Occasionally, Yandex.Disk can for some reason return an :code:`http://` link 164 | to an asynchronous operation instead of :code:`https://`. 165 | Both links are now recognized correctly and an :code:`https://` version will 166 | always be used by :code:`get_operation_status()`, regardless of which one 167 | Yandex.Disk returned. 168 | 169 | * **Release 1.3.2 (2021-07-10)** 170 | 171 | * Fixed :code:`__version__` having the old value 172 | 173 | * **Release 1.3.1 (2021-07-10)** 174 | 175 | * Fixed :code:`AttributeError` in :code:`SessionWithHeaders` (`PR #1`_) 176 | * Fixed trash bin tests 177 | 178 | * **Release 1.3.0 (2019-07-06)** 179 | 180 | * Modified the original library (`yadisk`_) to support `async/await` 181 | * The library was renamed to `yadisk-async` 182 | 183 | The following releases are for `yadisk`_, the original library: 184 | 185 | * **Release 1.2.14 (2019-03-26)** 186 | 187 | * Fixed a :code:`TypeError` in :code:`get_public_*` functions when passing :code:`path` parameter 188 | (see `issue #7`_) 189 | * Added :code:`unlimited_autoupload_enabled` attribute for :code:`DiskInfoObject` 190 | 191 | * **Release 1.2.13 (2019-02-23)** 192 | 193 | * Added :code:`md5` parameter for :code:`remove()` 194 | * Added :code:`UserPublicInfoObject` 195 | * Added :code:`country` attribute for :code:`UserObject` 196 | * Added :code:`photoslice_time` attribute for :code:`ResourceObject`, :code:`PublicResourceObject` 197 | and :code:`TrashResourceObject` 198 | 199 | * **Release 1.2.13 (2019-02-23)** 200 | 201 | * Added :code:`md5` parameter for :code:`remove()` 202 | * Added :code:`UserPublicInfoObject` 203 | * Added :code:`country` attribute for :code:`UserObject` 204 | * Added :code:`photoslice_time` attribute for :code:`ResourceObject`, :code:`PublicResourceObject` 205 | and :code:`TrashResourceObject` 206 | 207 | * **Release 1.2.12 (2018-10-11)** 208 | 209 | * Fixed `fields` parameter not working properly in `listdir()` (`issue #4`_) 210 | 211 | * **Release 1.2.11 (2018-06-30)** 212 | 213 | * Added the missing parameter :code:`sort` for :code:`get_meta()` 214 | * Added :code:`file` and :code:`antivirus_status` attributes for :code:`ResourceObject`, 215 | :code:`PublicResourceObject` and :code:`TrashResourceObject` 216 | * Added :code:`headers` parameter 217 | * Fixed a typo in :code:`download()` and :code:`download_public()` (`issue #2`_) 218 | * Removed :code:`*args` parameter everywhere 219 | 220 | * **Release 1.2.10 (2018-06-14)** 221 | 222 | * Fixed :code:`timeout=None` behavior. :code:`None` is supposed to mean 'no timeout' but 223 | in the older versions it was synonymous with the default timeout. 224 | 225 | * **Release 1.2.9 (2018-04-28)** 226 | 227 | * Changed the license to LGPLv3 (see :code:`COPYING` and :code:`COPYING.lesser`) 228 | * Other package info updates 229 | 230 | * **Release 1.2.8 (2018-04-17)** 231 | 232 | * Fixed a couple of typos: :code:`PublicResourceListObject.items` and 233 | :code:`TrashResourceListObject.items` had wrong types 234 | * Substitute field aliases in :code:`fields` parameter when performing 235 | API requests (e.g. :code:`embedded` -> :code:`_embedded`) 236 | 237 | * **Release 1.2.7 (2018-04-15)** 238 | 239 | * Fixed a file rewinding bug when uploading/downloading files after a retry 240 | 241 | * **Release 1.2.6 (2018-04-13)** 242 | 243 | * Now caching :code:`requests` sessions so that open connections 244 | can be reused (which can significantly speed things up sometimes) 245 | * Disable :code:`keep-alive` when uploading/downloading files by default 246 | 247 | * **Release 1.2.5 (2018-03-31)** 248 | 249 | * Fixed an off-by-one bug in :code:`utils.auto_retry()` 250 | (which could sometimes result in :code:`AttributeError`) 251 | * Retry the whole request for :code:`upload()`, :code:`download()` and :code:`download_public()` 252 | * Set :code:`stream=True` for :code:`download()` and :code:`download_public()` 253 | * Other minor fixes 254 | 255 | * **Release 1.2.4 (2018-02-19)** 256 | 257 | * Fixed :code:`TokenObject` having :code:`exprires_in` instead of :code:`expires_in` (fixed a typo) 258 | 259 | * **Release 1.2.3 (2018-01-20)** 260 | 261 | * Fixed a :code:`TypeError` when :code:`WrongResourceTypeError` is raised 262 | 263 | * **Release 1.2.2 (2018-01-19)** 264 | 265 | * :code:`refresh_token()` no longer requires a valid or empty token. 266 | 267 | * **Release 1.2.1 (2018-01-14)** 268 | 269 | * Fixed auto retries not working. Whoops. 270 | 271 | * **Release 1.2.0 (2018-01-14)** 272 | 273 | * Fixed passing :code:`n_retries=0` to :code:`upload()`, 274 | :code:`download()` and :code:`download_public()` 275 | * :code:`upload()`, :code:`download()` and :code:`download_public()` 276 | no longer return anything (see the docs) 277 | * Added :code:`utils` module (see the docs) 278 | * Added :code:`RetriableYaDiskError`, :code:`WrongResourceTypeError`, 279 | :code:`BadGatewayError` and :code:`GatewayTimeoutError` 280 | * :code:`listdir()` now raises :code:`WrongResourceTypeError` 281 | instead of :code:`NotADirectoryError` 282 | 283 | * **Release 1.1.1 (2017-12-29)** 284 | 285 | * Fixed argument handling in :code:`upload()`, :code:`download()` and :code:`download_public()`. 286 | Previously, passing :code:`n_retries` and :code:`retry_interval` would raise an exception (:code:`TypeError`). 287 | 288 | * **Release 1.1.0 (2017-12-27)** 289 | 290 | * Better exceptions (see the docs) 291 | * Added support for :code:`force_async` parameter 292 | * Minor bug fixes 293 | 294 | * **Release 1.0.8 (2017-11-29)** 295 | 296 | * Fixed yet another :code:`listdir()` bug 297 | 298 | * **Release 1.0.7 (2017-11-04)** 299 | 300 | * Added :code:`install_requires` argument to :code:`setup.py` 301 | 302 | * **Release 1.0.6 (2017-11-04)** 303 | 304 | * Return :code:`OperationLinkObject` in some functions 305 | 306 | * **Release 1.0.5 (2017-10-29)** 307 | 308 | * Fixed :code:`setup.py` to exclude tests 309 | 310 | * **Release 1.0.4 (2017-10-23)** 311 | 312 | * Fixed bugs in :code:`upload`, :code:`download` and :code:`listdir` functions 313 | * Set default :code:`listdir` :code:`limit` to :code:`10000` 314 | 315 | * **Release 1.0.3 (2017-10-22)** 316 | 317 | * Added settings 318 | 319 | * **Release 1.0.2 (2017-10-19)** 320 | 321 | * Fixed :code:`get_code_url` function (added missing parameters) 322 | 323 | * **Release 1.0.1 (2017-10-18)** 324 | 325 | * Fixed a major bug in :code:`GetTokenRequest` (added missing parameter) 326 | 327 | * **Release 1.0.0 (2017-10-18)** 328 | 329 | * Initial release 330 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | YaDisk-async 2 | ============ 3 | 4 | .. image:: https://img.shields.io/readthedocs/yadisk-async.svg 5 | :alt: Read the Docs 6 | :target: https://yadisk-async.readthedocs.io/en/latest/ 7 | 8 | .. image:: https://img.shields.io/pypi/v/yadisk-async.svg 9 | :alt: PyPI 10 | :target: https://pypi.org/project/yadisk-async 11 | 12 | YaDisk-async - это библиотека-клиент REST API Яндекс.Диска с поддержкой async/await. 13 | 14 | Данная библиотека больше не поддерживается, используйте 15 | `yadisk `__>=2.0.0 вместо неё, она также 16 | поддерживает асинхронный API. 17 | 18 | .. _Read the Docs (EN): http://yadisk-async.readthedocs.io 19 | .. _Read the Docs (RU): http://yadisk-async.readthedocs.io/ru/latest 20 | 21 | Документация доступна на `Read the Docs (RU)`_ и `Read the Docs (EN)`_. 22 | 23 | Установка 24 | ********* 25 | 26 | .. code:: bash 27 | 28 | pip install yadisk-async 29 | 30 | или 31 | 32 | .. code:: bash 33 | 34 | python setup.py install 35 | 36 | Примеры 37 | ******* 38 | 39 | .. code:: python 40 | 41 | import yadisk_async 42 | 43 | y = yadisk_async.YaDisk(token="") 44 | # или 45 | # y = yadisk_async.YaDisk("", "", "<токен>") 46 | 47 | # Проверяет, валиден ли токен 48 | print(await y.check_token()) 49 | 50 | # Получает общую информацию о диске 51 | print(await y.get_disk_info()) 52 | 53 | # Выводит содержимое "/some/path" 54 | print([i async for i in await y.listdir("/some/path")]) 55 | 56 | # Загружает "file_to_upload.txt" в "/destination.txt" 57 | await y.upload("file_to_upload.txt", "/destination.txt") 58 | 59 | # То же самое 60 | with open("file_to_upload.txt", "rb") as f: 61 | await y.upload(f, "/destination.txt") 62 | 63 | # То же самое, но с aiofiles 64 | async with aiofiles.open("file_to_upload.txt", "rb") as f: 65 | await y.upload(f, "/destination.txt") 66 | 67 | # Скачивает "/some-file-to-download.txt" в "downloaded.txt" 68 | await y.download("/some-file-to-download.txt", "downloaded.txt") 69 | 70 | # То же самое, но с aiofiles 71 | async with aiofiles.open("downloaded.txt", "wb") as f: 72 | await y.download("/some-file-to-download.txt", f) 73 | 74 | # Безвозвратно удаляет "/file-to-remove" 75 | await y.remove("/file-to-remove", permanently=True) 76 | 77 | # Создаёт новую папку "/test-dir" 78 | print(await y.mkdir("/test-dir")) 79 | 80 | # Всегда закрывайте все соединения или получите предупреждение 81 | await y.close() 82 | 83 | История изменений 84 | ***************** 85 | 86 | .. _yadisk: https://github.com/ivknv/yadisk 87 | 88 | .. _issue #2: https://github.com/ivknv/yadisk/issues/2 89 | .. _issue #4: https://github.com/ivknv/yadisk/issues/4 90 | .. _issue #7: https://github.com/ivknv/yadisk/issues/7 91 | .. _PR #1: https://github.com/ivknv/yadisk-async/pull/1 92 | .. _issue #23: https://github.com/ivknv/yadisk/issues/23 93 | .. _PR #6: https://github.com/ivknv/yadisk-async/pull/6 94 | .. _issue #26: https://github.com/ivknv/yadisk/issues/26 95 | .. _issue #28: https://github.com/ivknv/yadisk/issues/28 96 | .. _issue #29: https://github.com/ivknv/yadisk/issues/29 97 | .. _PR #31: https://github.com/ivknv/yadisk/pull/31 98 | 99 | * **Release 1.4.5 (2023-12-14)** 100 | 101 | * Добавлено предупреждение об устаревании при импорте 102 | * Данная библиотека больше не поддерживается, используйте `yadisk 103 | `__>=2.0.0 вместо неё, она также поддерживает 104 | асинхронный API 105 | 106 | * **Release 1.4.4 (2023-10-15)** 107 | 108 | * Методы :code:`upload()` и :code:`download()` (и связянные с ними) теперь 109 | могут загружать/скачивать файлы, не поддерживающие операцию :code:`seek()` 110 | (например, :code:`stdin` и :code:`stdout`, при условии, что они открыты в 111 | режиме :code:`"rb"` или :code:`"wb"`), см. `PR #31`_ 112 | 113 | * **Release 1.4.3 (2023-04-22)** 114 | 115 | * Пути вида :code:`app:/` теперь работают правильно (см. `issue #26`_) 116 | 117 | * **Release 1.4.2 (2023-03-20)** 118 | 119 | * Исправлено `issue #29`_: TypeError: 'type' object is not subscriptable 120 | * Исправлена ошибка (Python <3.9): TypeError: Too many parameters for typing.AsyncIterable; actual 2, expected 1 121 | 122 | * **Release 1.4.1 (2023-02-28)** 123 | 124 | * Исправлено `issue #28`_: :code:`TypeError` при вызове :code:`download_public()` с параметром :code:`path` 125 | * Исправлено :code:`AttributeError` при вызове :code:`ResourceLinkObject.public_listdir()` 126 | 127 | * **Release 1.4.0 (2023-01-30)** 128 | 129 | * Добавлены convenience-методы для объектов :code:`...Object` (например, см. :code:`ResourceObject`) 130 | * Добавлены подсказки типов (type hints) 131 | * Улучшены проверки ошибок и проверка ответа 132 | * Добавлены :code:`InvalidResponseError`, :code:`PayloadTooLargeError`, :code:`UploadTrafficLimitExceededError` 133 | * Добавлено несколько недостающих полей объектов :code:`DiskInfoObject` и :code:`SystemFoldersObject` 134 | * Добавлены методы :code:`rename()`, :code:`upload_by_link()` и :code:`download_by_link()` 135 | * Добавлен аттрибут :code:`default_args` объекта :code:`YaDisk` 136 | * :code:`download()` и :code:`upload()` теперь возвращают :code:`ResourceLinkObject` 137 | * До этого возвращаемые объекты :code:`LinkObject` были заменены более конкретными подклассами 138 | * :code:`TimeoutError` теперь тоже вызывает повторную попытку 139 | * Добавлена поддержка асинхронных файлов для :code:`download()` и :code:`upload()` 140 | * По умолчанию используется библиотека :code:`aiofiles` для открытия файлов 141 | 142 | * **Release 1.3.6 (2023-01-20)** 143 | 144 | * Исправлено `issue #26`_: символ ':' в именах файлов приводит к 145 | :code:`BadRequestError`. Это поведение вызвано работой самого REST API 146 | Яндекс.Диска, но было исправлено на уровне библиотеки. 147 | 148 | * **Release 1.3.5 (2022-11-10)** 149 | 150 | * Исправлен баг в :code:`is_file()` и :code:`is_dir()`: из-за опечатки функции 151 | вызывали исключение :code:`AttributeError` (`PR #6`_). 152 | 153 | * **Release 1.3.4 (2022-08-17)** 154 | 155 | * Исправлен баг в :code:`check_token()`: функция могла вызвать :code:`ForbiddenError`, 156 | если у приложения недостатчно прав (`issue #23`_). 157 | 158 | * **Release 1.3.3 (2021-12-31)** 159 | 160 | * Исправлено: не распознавались ссылки на асинхронные операции, если они 161 | использовали :code:`http://` (вместо :code:`https://`). 162 | Иногда Яндекс.Диск может вернуть :code:`http://` ссылку на асинхронную 163 | операцию. Теперь обе версии ссылок распознаются правильно, при этом, 164 | при получении информации об операции (через :code:`get_operation_status()`) 165 | всегда используется :code:`https://` версия ссылки, даже если Яндекс.Диск 166 | вернул :code:`http://`. 167 | 168 | * **Release 1.3.2 (2021-07-10)** 169 | 170 | * Исправлено значение :code:`__version__` 171 | 172 | * **Release 1.3.1 (2021-07-10)** 173 | 174 | * Исправлена ошибка :code:`AttributeError` в объекте :code:`SessionWithHeaders` (`PR #1`_) 175 | * Исправлены тесты корзины 176 | 177 | * **Release 1.3.0 (2019-07-06)** 178 | 179 | * Реализована поддержка `async/await` 180 | * Библиотека была переименована из `yadisk`_ в `yadisk-async` 181 | 182 | Следующие релизы относятся к оригинальной библиотеке `yadisk`_: 183 | 184 | * **Release 1.2.14 (2019-03-26)** 185 | 186 | * Исправлена ошибка :code:`TypeError` в функциях :code:`get_public_*` при 187 | использовании с параметром :code:`path` (`issue #7`_) 188 | * Добавлен аттрибут :code:`unlimited_autoupload_enabled` для :code:`DiskInfoObject` 189 | 190 | * **Release 1.2.13 (2019-02-23)** 191 | 192 | * Добавлен :code:`md5` параметр для :code:`remove()` 193 | * Добавлен :code:`UserPublicInfoObject` 194 | * Добавлен аттрибут :code:`country` для :code:`UserObject` 195 | * Добавлен аттрибут :code:`photoslice_time` для :code:`ResourceObject`, :code:`PublicResourceObject` 196 | и :code:`TrashResourceObject` 197 | 198 | * **Release 1.2.12 (2018-10-11)** 199 | 200 | * Исправлен баг: не работает параметр `fields` в `listdir()` (`issue #4`_) 201 | 202 | * **Release 1.2.11 (2018-06-30)** 203 | 204 | * Добавлен недостающий параметр :code:`sort` для :code:`get_meta()` 205 | * Добавлены аттрибуты :code:`file` и :code:`antivirus_status` для :code:`ResourceObject`, 206 | :code:`PublicResourceObject` и :code:`TrashResourceObject` 207 | * Добавлен параметр :code:`headers` 208 | * Исправлена опечатка в :code:`download()` и :code:`download_public()` (`issue #2`_) 209 | * Убран параметр :code:`*args` 210 | 211 | * **Release 1.2.10 (2018-06-14)** 212 | 213 | * Исправлено поведение :code:`timeout=None`. :code:`None` должен означать „без таймаута“, 214 | но в предыдущих версиях значение :code:`None` было синонимично со стандартным таймаутом. 215 | 216 | * **Release 1.2.9 (2018-04-28)** 217 | 218 | * Изменена лицензия на LGPLv3 (см. :code:`COPYING` и :code:`COPYING.lesser`) 219 | * Другие изменения информации о пакете 220 | 221 | * **Release 1.2.8 (2018-04-17)** 222 | 223 | * Исправлено несколько опечаток: у :code:`PublicResourceListObject.items` и 224 | :code:`TrashResourceListObject.items` были неправильные типы данных 225 | * Псевдонимы полей в параметре :code:`fields` заменяются при выполнении 226 | запросов API (например, :code:`embedded` -> :code:`_embedded`) 227 | 228 | * **Release 1.2.7 (2018-04-15)** 229 | 230 | * Исправлен баг перемотки файла при загрузке/скачивании после повторной попытки 231 | 232 | * **Release 1.2.6 (2018-04-13)** 233 | 234 | * Теперь объекты сессий :code:`requests` кэшируются, чтобы их можно 235 | было переиспользовать (иногда может существенно ускорить выполнение запросов) 236 | * :code:`keep-alive` отключается при загрузке/скачивании файлов по умолчанию 237 | 238 | * **Release 1.2.5 (2018-03-31)** 239 | 240 | * Исправлен баг (ошибка на единицу) в :code:`utils.auto_retry()` (иногда мог вызвать :code:`AttributeError`) 241 | * Повторные попытки применяются для :code:`upload()`, :code:`download()` и :code:`download_public()` целиком 242 | * Задано :code:`stream=True` для :code:`download()` и :code:`download_public()` 243 | * Другие мелкие исправления 244 | 245 | * **Release 1.2.4 (2018-02-19)** 246 | 247 | * Исправлена опечатка (:code:`TokenObject.exprires_in` -> :code:`TokenObject.expires_in`) 248 | 249 | * **Release 1.2.3 (2018-01-20)** 250 | 251 | * Исправлено :code:`TypeError` при вызове :code:`WrongResourceTypeError` 252 | 253 | * **Release 1.2.2 (2018-01-19)** 254 | 255 | * :code:`refresh_token()` больше не требует валидный или пустой токен. 256 | 257 | * **Release 1.2.1 (2018-01-14)** 258 | 259 | * Исправлена неработоспособность повторных попыток. 260 | 261 | * **Release 1.2.0 (2018-01-14)** 262 | 263 | * Исправлено использование :code:`n_retries=0` в :code:`upload()`, :code:`download()` и :code:`download_public()` 264 | * :code:`upload()`, :code:`download()` и :code:`download_public()` больше не возвращают ничего (см. документацию) 265 | * Добавлен модуль :code:`utils` (см. документацию) 266 | * Добавлены :code:`RetriableYaDiskError`, :code:`WrongResourceTypeError`, :code:`BadGatewayError` и :code:`GatewayTimeoutError` 267 | * :code:`listdir()` теперь вызывает :code:`WrongResourceTypeError` вместо :code:`NotADirectoryError` 268 | 269 | * **Release 1.1.1 (2017-12-29)** 270 | 271 | * Исправлена обработка аргументов в :code:`upload()`, :code:`download()` и :code:`download_public()`. 272 | До этого использование :code:`n_retries` и :code:`retry_interval` вызывало исключение (:code:`TypeError`). 273 | 274 | * **Release 1.1.0 (2017-12-27)** 275 | 276 | * Усовершенствованные исключения (см. документацию) 277 | * Добавлена поддержка параметра :code:`force_async` 278 | * Мелкие исправления багов 279 | 280 | * **Release 1.0.8 (2017-11-29)** 281 | 282 | * Исправлен ещё один баг в :code:`listdir()` 283 | 284 | * **Release 1.0.7 (2017-11-04)** 285 | 286 | * Добавлен :code:`install_requires` в :code:`setup.py` 287 | 288 | * **Release 1.0.6 (2017-11-04)** 289 | 290 | * Некоторые функции теперь возвращают :code:`OperationLinkObject` 291 | 292 | * **Release 1.0.5 (2017-10-29)** 293 | 294 | * Исправлен :code:`setup.py`, теперь исключает тесты 295 | 296 | * **Release 1.0.4 (2017-10-23)** 297 | 298 | * Исправлены баги в :code:`upload`, :code:`download` и :code:`listdir` 299 | * Значение по-умолчанию :code:`limit` в :code:`listdir` установлено в :code:`10000` 300 | 301 | * **Release 1.0.3 (2017-10-22)** 302 | 303 | * Добавлен модуль :code:`settings` 304 | 305 | * **Release 1.0.2 (2017-10-19)** 306 | 307 | * Исправлена функция :code:`get_code_url` (добавлены недостающие параметры) 308 | 309 | * **Release 1.0.1 (2017-10-18)** 310 | 311 | * Исправлен серьёзный баг в :code:`GetTokenRequest` (добавлен недостающий параметр) 312 | 313 | * **Release 1.0.0 (2017-10-18)** 314 | 315 | * Первый релиз 316 | -------------------------------------------------------------------------------- /README.ru.rst: -------------------------------------------------------------------------------- 1 | YaDisk-async 2 | ============ 3 | 4 | .. image:: https://img.shields.io/readthedocs/yadisk-async.svg 5 | :alt: Read the Docs 6 | :target: https://yadisk-async.readthedocs.io/en/latest/ 7 | 8 | .. image:: https://img.shields.io/pypi/v/yadisk-async.svg 9 | :alt: PyPI 10 | :target: https://pypi.org/project/yadisk-async 11 | 12 | YaDisk-async - это библиотека-клиент REST API Яндекс.Диска с поддержкой async/await. 13 | 14 | Данная библиотека больше не поддерживается, используйте 15 | `yadisk `__>=2.0.0 вместо неё, она также 16 | поддерживает асинхронный API. 17 | 18 | .. _Read the Docs (EN): http://yadisk-async.readthedocs.io 19 | .. _Read the Docs (RU): http://yadisk-async.readthedocs.io/ru/latest 20 | 21 | Документация доступна на `Read the Docs (RU)`_ и `Read the Docs (EN)`_. 22 | 23 | Установка 24 | ********* 25 | 26 | .. code:: bash 27 | 28 | pip install yadisk-async 29 | 30 | или 31 | 32 | .. code:: bash 33 | 34 | python setup.py install 35 | 36 | Примеры 37 | ******* 38 | 39 | .. code:: python 40 | 41 | import yadisk_async 42 | 43 | y = yadisk_async.YaDisk(token="") 44 | # или 45 | # y = yadisk_async.YaDisk("", "", "<токен>") 46 | 47 | # Проверяет, валиден ли токен 48 | print(await y.check_token()) 49 | 50 | # Получает общую информацию о диске 51 | print(await y.get_disk_info()) 52 | 53 | # Выводит содержимое "/some/path" 54 | print([i async for i in await y.listdir("/some/path")]) 55 | 56 | # Загружает "file_to_upload.txt" в "/destination.txt" 57 | await y.upload("file_to_upload.txt", "/destination.txt") 58 | 59 | # То же самое 60 | with open("file_to_upload.txt", "rb") as f: 61 | await y.upload(f, "/destination.txt") 62 | 63 | # То же самое, но с aiofiles 64 | async with aiofiles.open("file_to_upload.txt", "rb") as f: 65 | await y.upload(f, "/destination.txt") 66 | 67 | # Скачивает "/some-file-to-download.txt" в "downloaded.txt" 68 | await y.download("/some-file-to-download.txt", "downloaded.txt") 69 | 70 | # То же самое, но с aiofiles 71 | async with aiofiles.open("downloaded.txt", "wb") as f: 72 | await y.download("/some-file-to-download.txt", f) 73 | 74 | # Безвозвратно удаляет "/file-to-remove" 75 | await y.remove("/file-to-remove", permanently=True) 76 | 77 | # Создаёт новую папку "/test-dir" 78 | print(await y.mkdir("/test-dir")) 79 | 80 | # Всегда закрывайте все соединения или получите предупреждение 81 | await y.close() 82 | 83 | История изменений 84 | ***************** 85 | 86 | .. _yadisk: https://github.com/ivknv/yadisk 87 | 88 | .. _issue #2: https://github.com/ivknv/yadisk/issues/2 89 | .. _issue #4: https://github.com/ivknv/yadisk/issues/4 90 | .. _issue #7: https://github.com/ivknv/yadisk/issues/7 91 | .. _PR #1: https://github.com/ivknv/yadisk-async/pull/1 92 | .. _issue #23: https://github.com/ivknv/yadisk/issues/23 93 | .. _PR #6: https://github.com/ivknv/yadisk-async/pull/6 94 | .. _issue #26: https://github.com/ivknv/yadisk/issues/26 95 | .. _issue #28: https://github.com/ivknv/yadisk/issues/28 96 | .. _issue #29: https://github.com/ivknv/yadisk/issues/29 97 | .. _PR #31: https://github.com/ivknv/yadisk/pull/31 98 | 99 | * **Release 1.4.5 (2023-12-14)** 100 | 101 | * Добавлено предупреждение об устаревании при импорте 102 | * Данная библиотека больше не поддерживается, используйте `yadisk 103 | `__>=2.0.0 вместо неё, она также поддерживает 104 | асинхронный API 105 | 106 | * **Release 1.4.4 (2023-10-15)** 107 | 108 | * Методы :code:`upload()` и :code:`download()` (и связянные с ними) теперь 109 | могут загружать/скачивать файлы, не поддерживающие операцию :code:`seek()` 110 | (например, :code:`stdin` и :code:`stdout`, при условии, что они открыты в 111 | режиме :code:`"rb"` или :code:`"wb"`), см. `PR #31`_ 112 | 113 | * **Release 1.4.3 (2023-04-22)** 114 | 115 | * Пути вида :code:`app:/` теперь работают правильно (см. `issue #26`_) 116 | 117 | * **Release 1.4.2 (2023-03-20)** 118 | 119 | * Исправлено `issue #29`_: TypeError: 'type' object is not subscriptable 120 | * Исправлена ошибка (Python <3.9): TypeError: Too many parameters for typing.AsyncIterable; actual 2, expected 1 121 | 122 | * **Release 1.4.1 (2023-02-28)** 123 | 124 | * Исправлено `issue #28`_: :code:`TypeError` при вызове :code:`download_public()` с параметром :code:`path` 125 | * Исправлено :code:`AttributeError` при вызове :code:`ResourceLinkObject.public_listdir()` 126 | 127 | * **Release 1.4.0 (2023-01-30)** 128 | 129 | * Добавлены convenience-методы для объектов :code:`...Object` (например, см. :code:`ResourceObject`) 130 | * Добавлены подсказки типов (type hints) 131 | * Улучшены проверки ошибок и проверка ответа 132 | * Добавлены :code:`InvalidResponseError`, :code:`PayloadTooLargeError`, :code:`UploadTrafficLimitExceededError` 133 | * Добавлено несколько недостающих полей объектов :code:`DiskInfoObject` и :code:`SystemFoldersObject` 134 | * Добавлены методы :code:`rename()`, :code:`upload_by_link()` и :code:`download_by_link()` 135 | * Добавлен аттрибут :code:`default_args` объекта :code:`YaDisk` 136 | * :code:`download()` и :code:`upload()` теперь возвращают :code:`ResourceLinkObject` 137 | * До этого возвращаемые объекты :code:`LinkObject` были заменены более конкретными подклассами 138 | * :code:`TimeoutError` теперь тоже вызывает повторную попытку 139 | * Добавлена поддержка асинхронных файлов для :code:`download()` и :code:`upload()` 140 | * По умолчанию используется библиотека :code:`aiofiles` для открытия файлов 141 | 142 | * **Release 1.3.6 (2023-01-20)** 143 | 144 | * Исправлено `issue #26`_: символ ':' в именах файлов приводит к 145 | :code:`BadRequestError`. Это поведение вызвано работой самого REST API 146 | Яндекс.Диска, но было исправлено на уровне библиотеки. 147 | 148 | * **Release 1.3.5 (2022-11-10)** 149 | 150 | * Исправлен баг в :code:`is_file()` и :code:`is_dir()`: из-за опечатки функции 151 | вызывали исключение :code:`AttributeError` (`PR #6`_). 152 | 153 | * **Release 1.3.4 (2022-08-17)** 154 | 155 | * Исправлен баг в :code:`check_token()`: функция могла вызвать :code:`ForbiddenError`, 156 | если у приложения недостатчно прав (`issue #23`_). 157 | 158 | * **Release 1.3.3 (2021-12-31)** 159 | 160 | * Исправлено: не распознавались ссылки на асинхронные операции, если они 161 | использовали :code:`http://` (вместо :code:`https://`). 162 | Иногда Яндекс.Диск может вернуть :code:`http://` ссылку на асинхронную 163 | операцию. Теперь обе версии ссылок распознаются правильно, при этом, 164 | при получении информации об операции (через :code:`get_operation_status()`) 165 | всегда используется :code:`https://` версия ссылки, даже если Яндекс.Диск 166 | вернул :code:`http://`. 167 | 168 | * **Release 1.3.2 (2021-07-10)** 169 | 170 | * Исправлено значение :code:`__version__` 171 | 172 | * **Release 1.3.1 (2021-07-10)** 173 | 174 | * Исправлена ошибка :code:`AttributeError` в объекте :code:`SessionWithHeaders` (`PR #1`_) 175 | * Исправлены тесты корзины 176 | 177 | * **Release 1.3.0 (2019-07-06)** 178 | 179 | * Реализована поддержка `async/await` 180 | * Библиотека была переименована из `yadisk`_ в `yadisk-async` 181 | 182 | Следующие релизы относятся к оригинальной библиотеке `yadisk`_: 183 | 184 | * **Release 1.2.14 (2019-03-26)** 185 | 186 | * Исправлена ошибка :code:`TypeError` в функциях :code:`get_public_*` при 187 | использовании с параметром :code:`path` (`issue #7`_) 188 | * Добавлен аттрибут :code:`unlimited_autoupload_enabled` для :code:`DiskInfoObject` 189 | 190 | * **Release 1.2.13 (2019-02-23)** 191 | 192 | * Добавлен :code:`md5` параметр для :code:`remove()` 193 | * Добавлен :code:`UserPublicInfoObject` 194 | * Добавлен аттрибут :code:`country` для :code:`UserObject` 195 | * Добавлен аттрибут :code:`photoslice_time` для :code:`ResourceObject`, :code:`PublicResourceObject` 196 | и :code:`TrashResourceObject` 197 | 198 | * **Release 1.2.12 (2018-10-11)** 199 | 200 | * Исправлен баг: не работает параметр `fields` в `listdir()` (`issue #4`_) 201 | 202 | * **Release 1.2.11 (2018-06-30)** 203 | 204 | * Добавлен недостающий параметр :code:`sort` для :code:`get_meta()` 205 | * Добавлены аттрибуты :code:`file` и :code:`antivirus_status` для :code:`ResourceObject`, 206 | :code:`PublicResourceObject` и :code:`TrashResourceObject` 207 | * Добавлен параметр :code:`headers` 208 | * Исправлена опечатка в :code:`download()` и :code:`download_public()` (`issue #2`_) 209 | * Убран параметр :code:`*args` 210 | 211 | * **Release 1.2.10 (2018-06-14)** 212 | 213 | * Исправлено поведение :code:`timeout=None`. :code:`None` должен означать „без таймаута“, 214 | но в предыдущих версиях значение :code:`None` было синонимично со стандартным таймаутом. 215 | 216 | * **Release 1.2.9 (2018-04-28)** 217 | 218 | * Изменена лицензия на LGPLv3 (см. :code:`COPYING` и :code:`COPYING.lesser`) 219 | * Другие изменения информации о пакете 220 | 221 | * **Release 1.2.8 (2018-04-17)** 222 | 223 | * Исправлено несколько опечаток: у :code:`PublicResourceListObject.items` и 224 | :code:`TrashResourceListObject.items` были неправильные типы данных 225 | * Псевдонимы полей в параметре :code:`fields` заменяются при выполнении 226 | запросов API (например, :code:`embedded` -> :code:`_embedded`) 227 | 228 | * **Release 1.2.7 (2018-04-15)** 229 | 230 | * Исправлен баг перемотки файла при загрузке/скачивании после повторной попытки 231 | 232 | * **Release 1.2.6 (2018-04-13)** 233 | 234 | * Теперь объекты сессий :code:`requests` кэшируются, чтобы их можно 235 | было переиспользовать (иногда может существенно ускорить выполнение запросов) 236 | * :code:`keep-alive` отключается при загрузке/скачивании файлов по умолчанию 237 | 238 | * **Release 1.2.5 (2018-03-31)** 239 | 240 | * Исправлен баг (ошибка на единицу) в :code:`utils.auto_retry()` (иногда мог вызвать :code:`AttributeError`) 241 | * Повторные попытки применяются для :code:`upload()`, :code:`download()` и :code:`download_public()` целиком 242 | * Задано :code:`stream=True` для :code:`download()` и :code:`download_public()` 243 | * Другие мелкие исправления 244 | 245 | * **Release 1.2.4 (2018-02-19)** 246 | 247 | * Исправлена опечатка (:code:`TokenObject.exprires_in` -> :code:`TokenObject.expires_in`) 248 | 249 | * **Release 1.2.3 (2018-01-20)** 250 | 251 | * Исправлено :code:`TypeError` при вызове :code:`WrongResourceTypeError` 252 | 253 | * **Release 1.2.2 (2018-01-19)** 254 | 255 | * :code:`refresh_token()` больше не требует валидный или пустой токен. 256 | 257 | * **Release 1.2.1 (2018-01-14)** 258 | 259 | * Исправлена неработоспособность повторных попыток. 260 | 261 | * **Release 1.2.0 (2018-01-14)** 262 | 263 | * Исправлено использование :code:`n_retries=0` в :code:`upload()`, :code:`download()` и :code:`download_public()` 264 | * :code:`upload()`, :code:`download()` и :code:`download_public()` больше не возвращают ничего (см. документацию) 265 | * Добавлен модуль :code:`utils` (см. документацию) 266 | * Добавлены :code:`RetriableYaDiskError`, :code:`WrongResourceTypeError`, :code:`BadGatewayError` и :code:`GatewayTimeoutError` 267 | * :code:`listdir()` теперь вызывает :code:`WrongResourceTypeError` вместо :code:`NotADirectoryError` 268 | 269 | * **Release 1.1.1 (2017-12-29)** 270 | 271 | * Исправлена обработка аргументов в :code:`upload()`, :code:`download()` и :code:`download_public()`. 272 | До этого использование :code:`n_retries` и :code:`retry_interval` вызывало исключение (:code:`TypeError`). 273 | 274 | * **Release 1.1.0 (2017-12-27)** 275 | 276 | * Усовершенствованные исключения (см. документацию) 277 | * Добавлена поддержка параметра :code:`force_async` 278 | * Мелкие исправления багов 279 | 280 | * **Release 1.0.8 (2017-11-29)** 281 | 282 | * Исправлен ещё один баг в :code:`listdir()` 283 | 284 | * **Release 1.0.7 (2017-11-04)** 285 | 286 | * Добавлен :code:`install_requires` в :code:`setup.py` 287 | 288 | * **Release 1.0.6 (2017-11-04)** 289 | 290 | * Некоторые функции теперь возвращают :code:`OperationLinkObject` 291 | 292 | * **Release 1.0.5 (2017-10-29)** 293 | 294 | * Исправлен :code:`setup.py`, теперь исключает тесты 295 | 296 | * **Release 1.0.4 (2017-10-23)** 297 | 298 | * Исправлены баги в :code:`upload`, :code:`download` и :code:`listdir` 299 | * Значение по-умолчанию :code:`limit` в :code:`listdir` установлено в :code:`10000` 300 | 301 | * **Release 1.0.3 (2017-10-22)** 302 | 303 | * Добавлен модуль :code:`settings` 304 | 305 | * **Release 1.0.2 (2017-10-19)** 306 | 307 | * Исправлена функция :code:`get_code_url` (добавлены недостающие параметры) 308 | 309 | * **Release 1.0.1 (2017-10-18)** 310 | 311 | * Исправлен серьёзный баг в :code:`GetTokenRequest` (добавлен недостающий параметр) 312 | 313 | * **Release 1.0.0 (2017-10-18)** 314 | 315 | * Первый релиз 316 | -------------------------------------------------------------------------------- /tests/yadisk_test.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | import asyncio 5 | import os 6 | import tempfile 7 | import aiofiles 8 | 9 | import posixpath 10 | from unittest import TestCase 11 | from io import BytesIO 12 | 13 | import yadisk_async 14 | import yadisk_async.settings 15 | from yadisk_async.common import is_operation_link, ensure_path_has_schema 16 | from yadisk_async.api.operations import GetOperationStatusRequest 17 | from typing import Optional 18 | 19 | yadisk_async.settings.DEFAULT_N_RETRIES = 50 20 | 21 | __all__ = ["YaDiskTestCase"] 22 | 23 | loop: Optional[asyncio.AbstractEventLoop] = None 24 | 25 | def async_test(f): 26 | def wrapper(*args, **kwargs): 27 | loop.run_until_complete(f(*args, **kwargs)) 28 | 29 | return wrapper 30 | 31 | class YaDiskTestCase(TestCase): 32 | def __init__(self, *args, **kwargs): 33 | self.yadisk = None 34 | 35 | TestCase.__init__(self, *args, **kwargs) 36 | 37 | if not os.environ.get("PYTHON_YADISK_APP_TOKEN"): 38 | raise ValueError("Environment variable PYTHON_YADISK_APP_TOKEN must be set") 39 | 40 | if not os.environ.get("PYTHON_YADISK_TEST_ROOT"): 41 | raise ValueError("Environment variable PYTHON_YADISK_TEST_ROOT must be set") 42 | 43 | self.path: str = os.environ["PYTHON_YADISK_TEST_ROOT"] 44 | 45 | # Get rid of 'disk:/' prefix in the path and make it start with a slash 46 | # for consistency 47 | if self.path.startswith("disk:/"): 48 | self.path = posixpath.join("/", self.path[len("disk:/"):]) 49 | 50 | try: 51 | self.loop = asyncio.get_running_loop() 52 | except RuntimeError: 53 | self.loop = asyncio.new_event_loop() 54 | 55 | global loop 56 | loop = self.loop 57 | 58 | self.yadisk: yadisk_async.YaDisk = yadisk_async.YaDisk( 59 | os.environ.get("PYTHON_YADISK_APP_ID"), 60 | os.environ.get("PYTHON_YADISK_APP_SECRET"), 61 | os.environ["PYTHON_YADISK_APP_TOKEN"]) 62 | 63 | def __del__(self): 64 | if self.yadisk is None: 65 | return 66 | 67 | self.loop.run_until_complete(self.yadisk.close()) 68 | 69 | @async_test 70 | async def test_get_meta(self): 71 | self.assertIsInstance(await self.yadisk.get_meta(self.path), yadisk_async.objects.ResourceObject) 72 | 73 | @async_test 74 | async def test_listdir(self): 75 | names = ["dir1", "dir2", "dir3"] 76 | paths = [posixpath.join(self.path, name) for name in names] 77 | mkdir_tasks = [self.yadisk.mkdir(path) for path in paths] 78 | 79 | await asyncio.gather(*mkdir_tasks) 80 | 81 | async def get_result(): 82 | return [i.name async for i in await self.yadisk.listdir(self.path)] 83 | 84 | result = await get_result() 85 | 86 | remove_tasks = [self.yadisk.remove(path, permanently=True) for path in paths] 87 | 88 | await asyncio.gather(*remove_tasks) 89 | 90 | self.assertEqual(result, names) 91 | 92 | @async_test 93 | async def test_listdir_fields(self): 94 | names = ["dir1", "dir2", "dir3"] 95 | paths = [posixpath.join(self.path, name) for name in names] 96 | mkdir_tasks = [self.yadisk.mkdir(path) for path in paths] 97 | 98 | await asyncio.gather(*mkdir_tasks) 99 | 100 | async def get_result(): 101 | return [(i.name, i.type, i.file) 102 | async for i in await self.yadisk.listdir(self.path, fields=["name", "type"])] 103 | 104 | result = await get_result() 105 | 106 | remove_tasks = [self.yadisk.remove(path, permanently=True) for path in paths] 107 | 108 | await asyncio.gather(*remove_tasks) 109 | 110 | self.assertEqual(result, [(name, "dir", None) for name in names]) 111 | 112 | @async_test 113 | async def test_listdir_on_file(self): 114 | buf = BytesIO() 115 | buf.write(b"0" * 1000) 116 | buf.seek(0) 117 | 118 | path = posixpath.join(self.path, "zeroes.txt") 119 | 120 | await self.yadisk.upload(buf, path) 121 | 122 | with self.assertRaises(yadisk_async.exceptions.WrongResourceTypeError): 123 | [i async for i in await self.yadisk.listdir(path)] 124 | 125 | await self.yadisk.remove(path, permanently=True) 126 | 127 | @async_test 128 | async def test_listdir_with_limits(self): 129 | names = ["dir1", "dir2", "dir3"] 130 | paths = [posixpath.join(self.path, name) for name in names] 131 | mkdir_tasks = [self.yadisk.mkdir(path) for path in paths] 132 | 133 | await asyncio.gather(*mkdir_tasks) 134 | 135 | async def get_result(): 136 | return [i.name async for i in await self.yadisk.listdir(self.path, limit=1)] 137 | 138 | result = await get_result() 139 | 140 | remove_tasks = [self.yadisk.remove(path, permanently=True) for path in paths] 141 | 142 | await asyncio.gather(*remove_tasks) 143 | 144 | self.assertEqual(result, names) 145 | 146 | @async_test 147 | async def test_mkdir_and_exists(self): 148 | names = ["dir1", "dir2", "dir3"] 149 | paths = [posixpath.join(self.path, name) for name in names] 150 | 151 | async def check_existence(path): 152 | await self.yadisk.mkdir(path) 153 | self.assertTrue(await self.yadisk.exists(path)) 154 | 155 | await self.yadisk.remove(path, permanently=True) 156 | self.assertFalse(await self.yadisk.exists(path)) 157 | 158 | tasks = [check_existence(path) for path in paths] 159 | 160 | await asyncio.gather(*tasks) 161 | 162 | @async_test 163 | async def test_upload_and_download(self): 164 | buf1 = BytesIO() 165 | buf2 = tempfile.NamedTemporaryFile("w+b") 166 | 167 | def wrapper(): 168 | self.assertTrue(False) 169 | 170 | buf1.close = wrapper 171 | 172 | buf1.write(b"0" * 1024**2) 173 | buf1.seek(0) 174 | 175 | path = posixpath.join(self.path, "zeroes.txt") 176 | 177 | await self.yadisk.upload(buf1, path, overwrite=True, n_retries=50) 178 | await self.yadisk.download(path, buf2.name, n_retries=50) 179 | await self.yadisk.remove(path, permanently=True) 180 | 181 | buf1.seek(0) 182 | buf2.seek(0) 183 | 184 | self.assertEqual(buf1.read(), buf2.read()) 185 | 186 | @async_test 187 | async def test_upload_and_download_async(self): 188 | content = b"0" * 1024 ** 2 189 | async with aiofiles.tempfile.NamedTemporaryFile("wb+") as source: 190 | await source.write(content) 191 | await source.seek(0) 192 | 193 | path1 = posixpath.join(self.path, "zeroes.txt") 194 | path2 = posixpath.join(self.path, "zeroes_from_generator.txt") 195 | 196 | await self.yadisk.upload(source, path1, overwrite=True, n_retries=50) 197 | 198 | async def source_generator(): 199 | for _ in range(1024): 200 | yield b"0" * 1024 201 | 202 | await self.yadisk.upload(source_generator, path2, overwrite=True, n_retries=50) 203 | 204 | async with aiofiles.tempfile.NamedTemporaryFile("wb+") as destination: 205 | await self.yadisk.download(path1, destination, n_retries=50) 206 | await destination.seek(0) 207 | 208 | self.assertEqual(content, await destination.read()) 209 | await self.yadisk.remove(path1, permanently=True) 210 | 211 | await destination.seek(0) 212 | await destination.truncate() 213 | await self.yadisk.download(path2, destination, n_retries=50) 214 | await destination.seek(0) 215 | 216 | self.assertEqual(content, await destination.read()) 217 | await self.yadisk.remove(path2, permanently=True) 218 | 219 | @async_test 220 | async def test_check_token(self): 221 | self.assertTrue(await self.yadisk.check_token()) 222 | self.assertFalse(await self.yadisk.check_token("asdasdasd")) 223 | 224 | @async_test 225 | async def test_permanent_remove(self): 226 | path = posixpath.join(self.path, "dir") 227 | origin_path = "disk:" + path 228 | 229 | await self.yadisk.mkdir(path) 230 | await self.yadisk.remove(path, permanently=True) 231 | 232 | async for i in await self.yadisk.trash_listdir("/"): 233 | self.assertFalse(i.origin_path == origin_path) 234 | 235 | @async_test 236 | async def test_restore_trash(self): 237 | path = posixpath.join(self.path, "dir") 238 | origin_path = "disk:" + path 239 | 240 | await self.yadisk.mkdir(path) 241 | await self.yadisk.remove(path) 242 | 243 | trash_path = None 244 | 245 | async for i in await self.yadisk.trash_listdir("/"): 246 | if i.origin_path == origin_path: 247 | trash_path = i.path 248 | break 249 | 250 | self.assertTrue(trash_path is not None) 251 | 252 | await self.yadisk.restore_trash(trash_path, path) 253 | self.assertTrue(await self.yadisk.exists(path)) 254 | await self.yadisk.remove(path, permanently=True) 255 | 256 | @async_test 257 | async def test_move(self): 258 | path1 = posixpath.join(self.path, "dir1") 259 | path2 = posixpath.join(self.path, "dir2") 260 | await self.yadisk.mkdir(path1) 261 | await self.yadisk.move(path1, path2) 262 | 263 | self.assertTrue(await self.yadisk.exists(path2)) 264 | 265 | await self.yadisk.remove(path2, permanently=True) 266 | 267 | @async_test 268 | async def test_remove_trash(self): 269 | path = posixpath.join(self.path, "dir-to-remove") 270 | origin_path = "disk:" + path 271 | 272 | await self.yadisk.mkdir(path) 273 | await self.yadisk.remove(path) 274 | 275 | trash_path = None 276 | 277 | async for i in await self.yadisk.trash_listdir("/"): 278 | if i.origin_path == origin_path: 279 | trash_path = i.path 280 | break 281 | 282 | self.assertTrue(trash_path is not None) 283 | 284 | await self.yadisk.remove_trash(trash_path) 285 | self.assertFalse(await self.yadisk.trash_exists(trash_path)) 286 | 287 | @async_test 288 | async def test_publish_unpublish(self): 289 | path = self.path 290 | 291 | await self.yadisk.publish(path) 292 | self.assertIsNotNone((await self.yadisk.get_meta(path)).public_url) 293 | 294 | await self.yadisk.unpublish(path) 295 | self.assertIsNone((await self.yadisk.get_meta(path)).public_url) 296 | 297 | @async_test 298 | async def test_patch(self): 299 | path = self.path 300 | 301 | await self.yadisk.patch(path, {"test_property": "I'm a value!"}) 302 | self.assertEqual((await self.yadisk.get_meta(path)).custom_properties["test_property"], "I'm a value!") 303 | 304 | await self.yadisk.patch(path, {"test_property": None}) 305 | self.assertIsNone((await self.yadisk.get_meta(path)).custom_properties) 306 | 307 | @async_test 308 | async def test_issue7(self): 309 | # See https://github.com/ivknv/yadisk/issues/7 310 | 311 | try: 312 | await self.yadisk.public_listdir("any value here", path="any value here") 313 | except yadisk_async.exceptions.PathNotFoundError: 314 | pass 315 | 316 | @async_test 317 | async def test_is_operation_link(self): 318 | self.assertTrue(is_operation_link("https://cloud-api.yandex.net/v1/disk/operations/123asd")) 319 | self.assertTrue(is_operation_link("http://cloud-api.yandex.net/v1/disk/operations/123asd")) 320 | self.assertFalse(is_operation_link("https://cloud-api.yandex.net/v1/disk/operation/1283718")) 321 | self.assertFalse(is_operation_link("https://asd8iaysd89asdgiu")) 322 | self.assertFalse(is_operation_link("http://asd8iaysd89asdgiu")) 323 | 324 | @async_test 325 | async def test_get_operation_status_request_url(self): 326 | request = GetOperationStatusRequest( 327 | self.yadisk.get_session(), 328 | "https://cloud-api.yandex.net/v1/disk/operations/123asd") 329 | self.assertTrue(is_operation_link(request.url)) 330 | 331 | request = GetOperationStatusRequest( 332 | self.yadisk.get_session(), 333 | "http://cloud-api.yandex.net/v1/disk/operations/123asd") 334 | self.assertTrue(is_operation_link(request.url)) 335 | self.assertTrue(request.url.startswith("https://")) 336 | 337 | request = GetOperationStatusRequest( 338 | self.yadisk.get_session(), 339 | "https://asd8iaysd89asdgiu") 340 | self.assertTrue(is_operation_link(request.url)) 341 | self.assertTrue(request.url.startswith("https://")) 342 | 343 | @async_test 344 | async def test_is_file(self): 345 | # See https://github.com/ivknv/yadisk-async/pull/6 346 | buf1 = BytesIO() 347 | 348 | buf1.write(b"0" * 1024**2) 349 | buf1.seek(0) 350 | 351 | path = posixpath.join(self.path, "zeroes.txt") 352 | 353 | await self.yadisk.upload(buf1, path, overwrite=True, n_retries=50) 354 | self.assertTrue(await self.yadisk.is_file(path)) 355 | await self.yadisk.remove(path, permanently=True) 356 | 357 | def test_ensure_path_has_schema(self): 358 | # See https://github.com/ivknv/yadisk/issues/26 for more details 359 | 360 | self.assertEqual(ensure_path_has_schema("disk:"), "disk:/disk:") 361 | self.assertEqual(ensure_path_has_schema("trash:", default_schema="trash"), "trash:/trash:") 362 | self.assertEqual(ensure_path_has_schema("/asd:123"), "disk:/asd:123") 363 | self.assertEqual(ensure_path_has_schema("/asd:123", "trash"), "trash:/asd:123") 364 | self.assertEqual(ensure_path_has_schema("example/path"), "disk:/example/path") 365 | self.assertEqual(ensure_path_has_schema("app:/test"), "app:/test") 366 | 367 | @async_test 368 | async def test_upload_download_non_seekable(self): 369 | # It should be possible to upload/download non-seekable file objects (such as stdin/stdout) 370 | # See https://github.com/ivknv/yadisk/pull/31 for more details 371 | 372 | test_input_file = BytesIO(b"0" * 1000) 373 | test_input_file.seekable = lambda: False 374 | 375 | def seek(*args, **kwargs): 376 | raise NotImplementedError 377 | 378 | test_input_file.seek = seek 379 | 380 | dst_path = posixpath.join(self.path, "zeroes.txt") 381 | 382 | await self.yadisk.upload(test_input_file, dst_path, n_retries=50) 383 | 384 | test_output_file = BytesIO() 385 | test_output_file.seekable = lambda: False 386 | test_output_file.seek = seek 387 | 388 | await self.yadisk.download(dst_path, test_output_file, n_retries=50) 389 | 390 | await self.yadisk.remove(dst_path, permanently=True) 391 | 392 | self.assertEqual(test_input_file.tell(), 1000) 393 | self.assertEqual(test_output_file.tell(), 1000) 394 | -------------------------------------------------------------------------------- /docs/locales/ru/LC_MESSAGES/changelog.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) 2018, Ivan Konovalov 3 | # This file is distributed under the same license as the YaDisk package. 4 | # FIRST AUTHOR , 2018. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: YaDisk 1.4.4\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2023-12-14 23:16+0500\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "MIME-Version: 1.0\n" 16 | "Content-Type: text/plain; charset=utf-8\n" 17 | "Content-Transfer-Encoding: 8bit\n" 18 | "Generated-By: Babel 2.14.0\n" 19 | 20 | #: ../../changelog.rst:2 21 | msgid "Changelog" 22 | msgstr "История изменений" 23 | 24 | #: ../../changelog.rst:17 25 | msgid "**Release 1.4.5 (2023-12-14)**" 26 | msgstr "" 27 | 28 | #: ../../changelog.rst:19 29 | msgid "Added a deprecation warning on import" 30 | msgstr "Добавлено предупреждение об устаревании при импорте" 31 | 32 | #: ../../changelog.rst:20 33 | msgid "" 34 | "This library is no longer maintained, use `yadisk " 35 | "`__>=2.0.0 instead, it also supports " 36 | "async API" 37 | msgstr "Данная библиотека больше не поддерживается, используйте `yadisk " 38 | "`__>=2.0.0 вместо неё, она также поддерживает " 39 | "асинхронный API" 40 | 41 | #: ../../changelog.rst:24 42 | msgid "**Release 1.4.4 (2023-10-15)**" 43 | msgstr "" 44 | 45 | #: ../../changelog.rst:26 46 | msgid "" 47 | "`upload()` and `download()` (and related) methods can now upload/download" 48 | " non-seekable file-like objects (e.g. `stdin` or `stdout` when open in " 49 | "binary mode), see `PR #31`_" 50 | msgstr "" 51 | "Методы `upload()` и `download()` (и связянные с ними) теперь могут " 52 | "загружать/скачивать файлы, не поддерживающие операцию `seek()` (например," 53 | " `stdin` и `stdout`, при условии, что они открыты в режиме `\"rb\"` или " 54 | "`\"wb\"`), см. `PR #31`_" 55 | 56 | #: ../../changelog.rst:30 57 | msgid "**Release 1.4.3 (2023-04-22)**" 58 | msgstr "" 59 | 60 | #: ../../changelog.rst:32 61 | msgid "`app:/` paths now work correctly (see `issue #26`_)" 62 | msgstr "Пути вида `app:/` теперь работают правильно (см. `issue #26`_)" 63 | 64 | #: ../../changelog.rst:34 65 | msgid "**Release 1.4.2 (2023-03-20)**" 66 | msgstr "" 67 | 68 | #: ../../changelog.rst:36 69 | msgid "Fixed `issue #29`_: TypeError: 'type' object is not subscriptable" 70 | msgstr "Исправлено `issue #29`_: TypeError: 'type' object is not subscriptable" 71 | 72 | #: ../../changelog.rst:37 73 | msgid "" 74 | "Fixed a bug affecting Python <3.9: TypeError: Too many parameters for " 75 | "typing.AsyncIterable; actual 2, expected 1" 76 | msgstr "" 77 | "Исправлена ошибка (Python <3.9): TypeError: Too many parameters for " 78 | "typing.AsyncIterable; actual 2, expected 1" 79 | 80 | #: ../../changelog.rst:39 81 | msgid "**Release 1.4.1 (2023-02-28)**" 82 | msgstr "" 83 | 84 | #: ../../changelog.rst:41 85 | msgid "" 86 | "Fixed `issue #28`_: calling `download_public()` with `path` keyword " 87 | "argument raises `TypeError`" 88 | msgstr "" 89 | "Исправлено `issue #28`_: `TypeError` при вызове `download_public()` с " 90 | "параметром `path`" 91 | 92 | #: ../../changelog.rst:42 93 | msgid "" 94 | "Fixed `AttributeError` raised when calling " 95 | "`ResourceLinkObject.public_listdir()`" 96 | msgstr "" 97 | "Исправлено `AttributeError` при вызове " 98 | "`ResourceLinkObject.public_listdir()`" 99 | 100 | #: ../../changelog.rst:44 101 | msgid "**Release 1.4.0 (2023-01-30)**" 102 | msgstr "" 103 | 104 | #: ../../changelog.rst:46 105 | msgid "" 106 | "Added convenience methods to `...Object` objects (e.g. see " 107 | "`ResourceObject`)" 108 | msgstr "" 109 | "Добавлены convenience-методы для объектов `...Object` (например, см. " 110 | "`ResourceObject`)" 111 | 112 | #: ../../changelog.rst:47 113 | msgid "Added type hints" 114 | msgstr "Добавлены подсказки типов (type hints)" 115 | 116 | #: ../../changelog.rst:48 117 | msgid "Improved error checking and response validation" 118 | msgstr "Улучшены проверки ошибок и проверка ответа" 119 | 120 | #: ../../changelog.rst:49 121 | msgid "" 122 | "Added `InvalidResponseError`, `PayloadTooLargeError`, " 123 | "`UploadTrafficLimitExceededError`" 124 | msgstr "" 125 | "Добавлены `InvalidResponseError`, `PayloadTooLargeError`, " 126 | "`UploadTrafficLimitExceededError`" 127 | 128 | #: ../../changelog.rst:50 129 | msgid "Added a few missing fields to `DiskInfoObject` and `SystemFoldersObject`" 130 | msgstr "" 131 | "Добавлено несколько недостающих полей объектов `DiskInfoObject` и " 132 | "`SystemFoldersObject`" 133 | 134 | #: ../../changelog.rst:51 135 | msgid "Added `rename()`, `upload_by_link()` and `download_by_link()` methods" 136 | msgstr "Добавлены методы `rename()`, `upload_by_link()` и `download_by_link()`" 137 | 138 | #: ../../changelog.rst:52 139 | msgid "Added `default_args` field for `YaDisk` object" 140 | msgstr "Добавлен аттрибут `default_args` объекта `YaDisk`" 141 | 142 | #: ../../changelog.rst:53 143 | msgid "`download()` and `upload()` now return `ResourceLinkObject`" 144 | msgstr "`download()` и `upload()` теперь возвращают `ResourceLinkObject`" 145 | 146 | #: ../../changelog.rst:54 147 | msgid "" 148 | "Returned `LinkObject` instances have been replaced by more specific " 149 | "subclasses" 150 | msgstr "" 151 | "До этого возвращаемые объекты `LinkObject` были заменены более " 152 | "конкретными подклассами" 153 | 154 | #: ../../changelog.rst:55 155 | msgid ":any:`TimeoutError` now also triggers a retry" 156 | msgstr ":any:`TimeoutError` теперь тоже вызывает повторную попытку" 157 | 158 | #: ../../changelog.rst:56 159 | msgid "Added support for async files for `download()` and `upload()`" 160 | msgstr "Добавлена поддержка асинхронных файлов для `download()` и `upload()`" 161 | 162 | #: ../../changelog.rst:57 163 | msgid "Use `aiofiles` to open files by default" 164 | msgstr "По умолчанию используется библиотека `aiofiles` для открытия файлов" 165 | 166 | #: ../../changelog.rst:59 167 | msgid "**Release 1.3.6 (2023-01-20)**" 168 | msgstr "" 169 | 170 | #: ../../changelog.rst:61 171 | msgid "" 172 | "Fixed `issue #26`_: ':' character in filenames causes `BadRequestError`. " 173 | "This is due the behavior of Yandex.Disk's REST API itself but is avoided " 174 | "on the library level with this fix." 175 | msgstr "" 176 | "Исправлено `issue #26`_: символ ':' в именах файлов приводит к " 177 | "`BadRequestError`. Это поведение вызвано работой самого REST API " 178 | "Яндекс.Диска, но было исправлено на уровне библиотеки." 179 | 180 | #: ../../changelog.rst:65 181 | msgid "**Release 1.3.5 (2022-11-10)**" 182 | msgstr "" 183 | 184 | #: ../../changelog.rst:67 185 | msgid "" 186 | "Fixed a bug in `is_file()` and `is_dir()`: a typo was causing " 187 | "`AttributeError` (`PR #6`_)." 188 | msgstr "" 189 | "Исправлен баг в `is_file()` и `is_dir()`: из-за опечатки функции вызывали" 190 | " исключение `AttributeError` (`PR #6`_)." 191 | 192 | #: ../../changelog.rst:70 193 | msgid "**Release 1.3.4 (2022-08-17)**" 194 | msgstr "" 195 | 196 | #: ../../changelog.rst:72 197 | msgid "" 198 | "Fixed a bug in `check_token()`: could throw `ForbiddenError` if the " 199 | "application lacks necessary permissions (`issue #23`_)." 200 | msgstr "" 201 | "Исправлен баг в `check_token()`: функция могла вызвать " 202 | "`ForbiddenError`,если у приложения недостатчно прав (`issue #23`_)." 203 | 204 | #: ../../changelog.rst:75 205 | msgid "**Release 1.3.3 (2021-12-31)**" 206 | msgstr "" 207 | 208 | #: ../../changelog.rst:77 209 | msgid "" 210 | "Fixed an issue where `http://` links were not recognized as operation " 211 | "links (they were assumed to always be `https://`, since all the other " 212 | "requests are always HTTPS). Occasionally, Yandex.Disk can for some reason" 213 | " return an `http://` link to an asynchronous operation instead of " 214 | "`https://`. Both links are now recognized correctly and an `https://` " 215 | "version will always be used by `get_operation_status()`, regardless of " 216 | "which one Yandex.Disk returned." 217 | msgstr "" 218 | "Исправлено: не распознавались ссылки на асинхронные операции, если они " 219 | "использовали `http://` (вместо `https://`). Иногда Яндекс.Диск может " 220 | "вернуть `http://` ссылку на асинхронную операцию. Теперь обе версии " 221 | "ссылок распознаются правильно, при этом, при получении информации об " 222 | "операции (через `get_operation_status()`) всегда используется `https://` " 223 | "версия ссылки, даже если Яндекс.Диск вернул `http://`." 224 | 225 | #: ../../changelog.rst:86 226 | msgid "**Release 1.3.2 (2021-07-10)**" 227 | msgstr "" 228 | 229 | #: ../../changelog.rst:88 230 | msgid "Fixed `__version__` having the wrong value" 231 | msgstr "Исправлено значение `__version__`" 232 | 233 | #: ../../changelog.rst:90 234 | msgid "**Release 1.3.1 (2021-07-10)**" 235 | msgstr "" 236 | 237 | #: ../../changelog.rst:92 238 | msgid "Fixed `AttributeError` in `SessionWithHeaders` (`PR #1`_)" 239 | msgstr "" 240 | "Исправлена ошибка `AttributeError` в объекте `SessionWithHeaders` (`PR " 241 | "#1`_)" 242 | 243 | #: ../../changelog.rst:93 244 | msgid "Fixed trash bin tests" 245 | msgstr "Исправлены тесты корзины" 246 | 247 | #: ../../changelog.rst:95 248 | msgid "**Release 1.3.0 (2019-07-06)**" 249 | msgstr "" 250 | 251 | #: ../../changelog.rst:97 252 | msgid "Modified the original library (`yadisk`_) to support `async/await`" 253 | msgstr "Реализована поддержка `async/await`" 254 | 255 | #: ../../changelog.rst:98 256 | msgid "The library was renamed to `yadisk-async`" 257 | msgstr "Библиотека была переименована из `yadisk`_ в `yadisk-async`" 258 | 259 | #: ../../changelog.rst:100 260 | msgid "The following releases are for `yadisk`_, the original library:" 261 | msgstr "Следующие релизы относятся к оригинальной библиотеке `yadisk`_:" 262 | 263 | #: ../../changelog.rst:102 264 | msgid "**Release 1.2.14 (2019-03-26)**" 265 | msgstr "" 266 | 267 | #: ../../changelog.rst:104 268 | msgid "" 269 | "Fixed a `TypeError` in `get_public_*` functions when passing `path` " 270 | "parameter (see `issue #7`_)" 271 | msgstr "" 272 | "Исправлена ошибка `TypeError` в функциях `get_public_*` при использовании" 273 | " с параметром `path` (`issue #7`_)" 274 | 275 | #: ../../changelog.rst:106 276 | msgid "Added `unlimited_autoupload_enabled` attribute for `DiskInfoObject`" 277 | msgstr "Добавлен аттрибут `unlimited_autoupload_enabled` для `DiskInfoObject`" 278 | 279 | #: ../../changelog.rst:108 280 | msgid "**Release 1.2.13 (2019-02-23)**" 281 | msgstr "" 282 | 283 | #: ../../changelog.rst:110 284 | msgid "Added `md5` parameter for `remove()`" 285 | msgstr "Добавлен параметр `md5` для `remove()`" 286 | 287 | #: ../../changelog.rst:111 288 | msgid "Added `UserPublicInfoObject`" 289 | msgstr "Добавлен `UserPublicInfoObject`" 290 | 291 | #: ../../changelog.rst:112 292 | msgid "Added `country` attribute for `UserObject`" 293 | msgstr "Добавлен аттрибут `country` для `UserObject`" 294 | 295 | #: ../../changelog.rst:113 296 | msgid "" 297 | "Added `photoslice_time` attribute for `ResourceObject`, " 298 | "`PublicResourceObject` and `TrashResourceObject`" 299 | msgstr "" 300 | "Добавлен аттрибут `photoslice_time` для `ResourceObject`, " 301 | "`PublicResourceObject` и `TrashResourceObject`" 302 | 303 | #: ../../changelog.rst:116 304 | msgid "**Release 1.2.12 (2018-10-11)**" 305 | msgstr "" 306 | 307 | #: ../../changelog.rst:118 308 | msgid "Fixed `fields` parameter not working properly in `listdir()` (`issue #4`_)" 309 | msgstr "Исправлен баг: не работает параметр `fields` в `listdir()` (`issue #4`_)" 310 | 311 | #: ../../changelog.rst:120 312 | msgid "**Release 1.2.11 (2018-06-30)**" 313 | msgstr "" 314 | 315 | #: ../../changelog.rst:122 316 | msgid "Added the missing parameter `sort` for `get_meta()`" 317 | msgstr "Добавлен недостающий параметр `sort` для `get_meta()`" 318 | 319 | #: ../../changelog.rst:123 320 | msgid "" 321 | "Added `file` and `antivirus_status` attributes for `ResourceObject`, " 322 | "`PublicResourceObject` and `TrashResourceObject`" 323 | msgstr "" 324 | "Добавлены аттрибуты `file` и `antivirus_status` для `ResourceObject`, " 325 | "`PublicResourceObject` и `TrashResourceObject`" 326 | 327 | #: ../../changelog.rst:125 328 | msgid "Added `headers` parameter" 329 | msgstr "Добавлен параметр `headers`" 330 | 331 | #: ../../changelog.rst:126 332 | msgid "Fixed a typo in `download()` and `download_public()` (`issue #2`_)" 333 | msgstr "Исправлена опечатка в `download()` и `download_public()` (`issue #2`_)" 334 | 335 | #: ../../changelog.rst:127 336 | msgid "Removed `*args` parameter everywhere" 337 | msgstr "Убран параметр `*args`" 338 | 339 | #: ../../changelog.rst:129 340 | msgid "**Release 1.2.10 (2018-06-14)**" 341 | msgstr "" 342 | 343 | #: ../../changelog.rst:131 344 | msgid "" 345 | "Fixed `timeout=None` behavior. `None` is supposed to mean 'no timeout' " 346 | "but in the older versions it was synonymous with the default timeout." 347 | msgstr "" 348 | "Исправлено поведение `timeout=None`. `None` должен означать 'без " 349 | "таймаута', но в предыдущих версиях значение `None` было синонимично со " 350 | "стандартным таймаутом." 351 | 352 | #: ../../changelog.rst:134 353 | msgid "**Release 1.2.9 (2018-04-28)**" 354 | msgstr "" 355 | 356 | #: ../../changelog.rst:136 357 | msgid "Changed the license to LGPLv3 (see `COPYING` and `COPYING.lesser`)" 358 | msgstr "Изменена лицензия на LGPLv3 (см. `COPYING` и `COPYING.lesser`)" 359 | 360 | #: ../../changelog.rst:137 361 | msgid "Other package info updates" 362 | msgstr "Другие изменения информации о пакете" 363 | 364 | #: ../../changelog.rst:139 365 | msgid "**Release 1.2.8 (2018-04-17)**" 366 | msgstr "" 367 | 368 | #: ../../changelog.rst:141 369 | msgid "" 370 | "Fixed a couple of typos: `PublicResourceListObject.items` and " 371 | "`TrashResourceListObject.items` had wrong types" 372 | msgstr "" 373 | "Исправлено несколько опечаток: у `PublicResourceListObject.items` и " 374 | "`TrashResourceListObject.items` были неправильные типы данных" 375 | 376 | #: ../../changelog.rst:143 377 | msgid "" 378 | "Substitute field aliases in `fields` parameter when performing API " 379 | "requests (e.g. `embedded` -> `_embedded`)" 380 | msgstr "" 381 | "Псевдонимы полей в параметре `fields` заменяются при выполнении запросов " 382 | "API (например, `embedded` -> `_embedded`)" 383 | 384 | #: ../../changelog.rst:146 385 | msgid "**Release 1.2.7 (2018-04-15)**" 386 | msgstr "" 387 | 388 | #: ../../changelog.rst:148 389 | msgid "Fixed a file rewinding bug when uploading/downloading files after a retry" 390 | msgstr "" 391 | "Исправлен баг перемотки файла при загрузке/скачивании после повторной " 392 | "попытки" 393 | 394 | #: ../../changelog.rst:150 395 | msgid "**Release 1.2.6 (2018-04-13)**" 396 | msgstr "" 397 | 398 | #: ../../changelog.rst:152 399 | msgid "" 400 | "Now caching `requests` sessions so that open connections can be reused " 401 | "(which can significantly speed things up sometimes)" 402 | msgstr "" 403 | "Теперь объекты сессий `requests` кэшируются, чтобы их можно было " 404 | "переиспользовать (иногда может существенно ускорить выполнение запросов)" 405 | 406 | #: ../../changelog.rst:154 407 | msgid "Disable `keep-alive` when uploading/downloading files by default" 408 | msgstr "`keep-alive` отключается при загрузке/скачивании файлов по умолчанию" 409 | 410 | #: ../../changelog.rst:156 411 | msgid "**Release 1.2.5 (2018-03-31)**" 412 | msgstr "" 413 | 414 | #: ../../changelog.rst:158 415 | msgid "" 416 | "Fixed an off-by-one bug in `utils.auto_retry()` (which could sometimes " 417 | "result in `AttributeError`)" 418 | msgstr "" 419 | "Исправлен баг (ошибка на единицу) в `utils.auto_retry()` (иногда мог " 420 | "вызвать `AttributeError`)" 421 | 422 | #: ../../changelog.rst:160 423 | msgid "" 424 | "Retry the whole request for `upload()`, `download()` and " 425 | "`download_public()`" 426 | msgstr "" 427 | "Повторные попытки применяются для `upload()`, `download()` и " 428 | "`download_public()` целиком" 429 | 430 | #: ../../changelog.rst:161 431 | msgid "Set `stream=True` for `download()` and `download_public()`" 432 | msgstr "Задано `stream=True` для `download()` и `download_public()`" 433 | 434 | #: ../../changelog.rst:162 435 | msgid "Other minor fixes" 436 | msgstr "Другие мелкие исправления" 437 | 438 | #: ../../changelog.rst:164 439 | msgid "**Release 1.2.4 (2018-02-19)**" 440 | msgstr "" 441 | 442 | #: ../../changelog.rst:166 443 | msgid "" 444 | "Fixed `TokenObject` having `exprires_in` instead of `expires_in` (fixed a" 445 | " typo)" 446 | msgstr "" 447 | "Исправлена опечатка (`TokenObject.exprires_in` -> " 448 | "`TokenObject.expires_in`)" 449 | 450 | #: ../../changelog.rst:168 451 | msgid "**Release 1.2.3 (2018-01-20)**" 452 | msgstr "" 453 | 454 | #: ../../changelog.rst:170 455 | msgid "Fixed a `TypeError` when `WrongResourceTypeError` is raised" 456 | msgstr "Исправлено `TypeError` при вызове `WrongResourceTypeError`" 457 | 458 | #: ../../changelog.rst:172 459 | msgid "**Release 1.2.2 (2018-01-19)**" 460 | msgstr "" 461 | 462 | #: ../../changelog.rst:174 463 | msgid "`refresh_token()` no longer requires a valid or empty token." 464 | msgstr "`refresh_token()` больше не требует валидный или пустой токен." 465 | 466 | #: ../../changelog.rst:176 467 | msgid "**Release 1.2.1 (2018-01-14)**" 468 | msgstr "" 469 | 470 | #: ../../changelog.rst:178 471 | msgid "Fixed auto retries not working. Whoops." 472 | msgstr "Исправлена неработоспособность повторных попыток." 473 | 474 | #: ../../changelog.rst:180 475 | msgid "**Release 1.2.0 (2018-01-14)**" 476 | msgstr "" 477 | 478 | #: ../../changelog.rst:182 479 | msgid "" 480 | "Fixed passing `n_retries=0` to `upload()`, `download()` and " 481 | "`download_public()`" 482 | msgstr "" 483 | "Исправлено использование `n_retries=0` в `upload()`, `download()` и " 484 | "`download_public()`" 485 | 486 | #: ../../changelog.rst:184 487 | msgid "" 488 | "`upload()`, `download()` and `download_public()` no longer return " 489 | "anything (see the docs)" 490 | msgstr "" 491 | "`upload()`, `download()` и `download_public()` больше не возвращают " 492 | "ничего (см. документацию)" 493 | 494 | #: ../../changelog.rst:186 495 | msgid "Added `utils` module (see the docs)" 496 | msgstr "Добавлен модуль `utils` (см. документацию)" 497 | 498 | #: ../../changelog.rst:187 499 | msgid "" 500 | "Added `RetriableYaDiskError`, `WrongResourceTypeError`, `BadGatewayError`" 501 | " and `GatewayTimeoutError`" 502 | msgstr "" 503 | "Добавлены `RetriableYaDiskError`, `WrongResourceTypeError`, " 504 | "`BadGatewayError` и `GatewayTimeoutError`" 505 | 506 | #: ../../changelog.rst:189 507 | msgid "" 508 | "`listdir()` now raises `WrongResourceTypeError` instead of " 509 | "`NotADirectoryError`" 510 | msgstr "" 511 | "`listdir()` теперь вызывает `WrongResourceTypeError` вместо " 512 | "`NotADirectoryError`" 513 | 514 | #: ../../changelog.rst:192 515 | msgid "**Release 1.1.1 (2017-12-29)**" 516 | msgstr "" 517 | 518 | #: ../../changelog.rst:194 519 | msgid "" 520 | "Fixed argument handling in `upload()`, `download()` and " 521 | "`download_public()`. Previously, passing `n_retries` and `retry_interval`" 522 | " would raise an exception (`TypeError`)." 523 | msgstr "" 524 | "Исправлена обработка аргументов в `upload()`, `download()` и " 525 | "`download_public()`. До этого использование `n_retries` и " 526 | "`retry_interval` вызывало исключение (`TypeError`)." 527 | 528 | #: ../../changelog.rst:197 529 | msgid "**Release 1.1.0 (2017-12-27)**" 530 | msgstr "" 531 | 532 | #: ../../changelog.rst:199 533 | msgid "Better exceptions (see the docs)" 534 | msgstr "Усовершенствованные исключения (см. документацию)" 535 | 536 | #: ../../changelog.rst:200 537 | msgid "Added support for `force_async` parameter" 538 | msgstr "Добавлена поддержка параметра `force_async`" 539 | 540 | #: ../../changelog.rst:201 541 | msgid "Minor bug fixes" 542 | msgstr "Мелкие исправления багов" 543 | 544 | #: ../../changelog.rst:203 545 | msgid "**Release 1.0.8 (2017-11-29)**" 546 | msgstr "" 547 | 548 | #: ../../changelog.rst:205 549 | msgid "Fixed yet another `listdir()` bug" 550 | msgstr "Исправлен ещё один баг в `listdir()`" 551 | 552 | #: ../../changelog.rst:207 553 | msgid "**Release 1.0.7 (2017-11-04)**" 554 | msgstr "" 555 | 556 | #: ../../changelog.rst:209 557 | msgid "Added `install_requires` argument to `setup.py`" 558 | msgstr "Добавлен `install_requires` в `setup.py`" 559 | 560 | #: ../../changelog.rst:211 561 | msgid "**Release 1.0.6 (2017-11-04)**" 562 | msgstr "" 563 | 564 | #: ../../changelog.rst:213 565 | msgid "Return `OperationLinkObject` in some functions" 566 | msgstr "Некоторые функции теперь возвращают `OperationLinkObject`" 567 | 568 | #: ../../changelog.rst:215 569 | msgid "**Release 1.0.5 (2017-10-29)**" 570 | msgstr "" 571 | 572 | #: ../../changelog.rst:217 573 | msgid "Fixed `setup.py` to exclude tests" 574 | msgstr "Исправлен `setup.py`, теперь исключает тесты" 575 | 576 | #: ../../changelog.rst:219 577 | msgid "**Release 1.0.4 (2017-10-23)**" 578 | msgstr "" 579 | 580 | #: ../../changelog.rst:221 581 | msgid "Fixed bugs in `upload`, `download` and `listdir` functions" 582 | msgstr "Исправлены баги в `upload`, `download` и `listdir`" 583 | 584 | #: ../../changelog.rst:222 585 | msgid "Set default `listdir` `limit` to `10000`" 586 | msgstr "Значение по-умолчанию `limit` в `listdir` установлено в `10000`" 587 | 588 | #: ../../changelog.rst:224 589 | msgid "**Release 1.0.3 (2017-10-22)**" 590 | msgstr "" 591 | 592 | #: ../../changelog.rst:226 593 | msgid "Added settings" 594 | msgstr "Добавлен модуль `settings`" 595 | 596 | #: ../../changelog.rst:228 597 | msgid "**Release 1.0.2 (2017-10-19)**" 598 | msgstr "" 599 | 600 | #: ../../changelog.rst:230 601 | msgid "Fixed `get_code_url` function (added missing parameters)" 602 | msgstr "Исправлена функция `get_code_url` (добавлены недостающие параметры)" 603 | 604 | #: ../../changelog.rst:232 605 | msgid "**Release 1.0.1 (2017-10-18)**" 606 | msgstr "" 607 | 608 | #: ../../changelog.rst:234 609 | msgid "Fixed a major bug in `GetTokenRequest` (added missing parameter)" 610 | msgstr "" 611 | "Исправлен серьёзный баг в `GetTokenRequest` (добавлен недостающий " 612 | "параметр)" 613 | 614 | #: ../../changelog.rst:236 615 | msgid "**Release 1.0.0 (2017-10-18)**" 616 | msgstr "" 617 | 618 | #: ../../changelog.rst:238 619 | msgid "Initial release" 620 | msgstr "Первый релиз" 621 | 622 | #~ msgid "Change the license to LGPLv3 (see `COPYING` and `COPYING.lesser`)" 623 | #~ msgstr "" 624 | 625 | #~ msgid "" 626 | #~ "Added convenience methods to `...Object` " 627 | #~ "objects (e.g. see " 628 | #~ ":any:`yadisk_async.objects.ResourceObject`)" 629 | #~ msgstr "" 630 | 631 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | --------------------------------------------------------------------------------