mirror of
https://github.com/robweber/xbmcbackup.git
synced 2024-12-22 05:55:23 +01:00
for python 3 need version 9+ of Dropbox
This commit is contained in:
parent
7ede17fbbd
commit
7d895a6028
2
.settings/org.eclipse.core.resources.prefs
Normal file
2
.settings/org.eclipse.core.resources.prefs
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
eclipse.preferences.version=1
|
||||||
|
encoding//resources/lib/croniter.py=utf-8
|
@ -6,7 +6,7 @@
|
|||||||
<import addon="script.module.kodi-six" version="0.1.0"/>
|
<import addon="script.module.kodi-six" version="0.1.0"/>
|
||||||
<import addon="script.module.future" version="0.16.0.4"/>
|
<import addon="script.module.future" version="0.16.0.4"/>
|
||||||
<import addon="script.module.six" version="1.11.0"/>
|
<import addon="script.module.six" version="1.11.0"/>
|
||||||
<import addon="script.module.dropbox" version="8.4.2"/>
|
<import addon="script.module.requests" version="2.18.4"/>
|
||||||
</requires>
|
</requires>
|
||||||
<extension point="xbmc.python.script" library="default.py">
|
<extension point="xbmc.python.script" library="default.py">
|
||||||
<provides>executable</provides>
|
<provides>executable</provides>
|
||||||
|
4
resources/lib/dropbox/__init__.py
Normal file
4
resources/lib/dropbox/__init__.py
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
from __future__ import absolute_import
|
||||||
|
|
||||||
|
from .dropbox import __version__, Dropbox, DropboxTeam, create_session # noqa: F401
|
||||||
|
from .oauth import DropboxOAuth2Flow, DropboxOAuth2FlowNoRedirect # noqa: F401
|
7
resources/lib/dropbox/async.py
Normal file
7
resources/lib/dropbox/async.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Auto-generated by Stone, do not modify.
|
||||||
|
# @generated
|
||||||
|
# flake8: noqa
|
||||||
|
# pylint: skip-file
|
||||||
|
# If you have issues importing this module because Python recognizes it as a keyword, use async_ instead.
|
||||||
|
from .async_ import *
|
862
resources/lib/dropbox/auth.py
Normal file
862
resources/lib/dropbox/auth.py
Normal file
@ -0,0 +1,862 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Auto-generated by Stone, do not modify.
|
||||||
|
# @generated
|
||||||
|
# flake8: noqa
|
||||||
|
# pylint: skip-file
|
||||||
|
try:
|
||||||
|
from . import stone_validators as bv
|
||||||
|
from . import stone_base as bb
|
||||||
|
except (ImportError, SystemError, ValueError):
|
||||||
|
# Catch errors raised when importing a relative module when not in a package.
|
||||||
|
# This makes testing this file directly (outside of a package) easier.
|
||||||
|
import stone_validators as bv
|
||||||
|
import stone_base as bb
|
||||||
|
|
||||||
|
class AccessError(bb.Union):
|
||||||
|
"""
|
||||||
|
Error occurred because the account doesn't have permission to access the
|
||||||
|
resource.
|
||||||
|
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar InvalidAccountTypeError AccessError.invalid_account_type: Current
|
||||||
|
account type cannot access the resource.
|
||||||
|
:ivar PaperAccessError AccessError.paper_access_denied: Current account
|
||||||
|
cannot access Paper.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def invalid_account_type(cls, val):
|
||||||
|
"""
|
||||||
|
Create an instance of this class set to the ``invalid_account_type`` tag
|
||||||
|
with value ``val``.
|
||||||
|
|
||||||
|
:param InvalidAccountTypeError val:
|
||||||
|
:rtype: AccessError
|
||||||
|
"""
|
||||||
|
return cls('invalid_account_type', val)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def paper_access_denied(cls, val):
|
||||||
|
"""
|
||||||
|
Create an instance of this class set to the ``paper_access_denied`` tag
|
||||||
|
with value ``val``.
|
||||||
|
|
||||||
|
:param PaperAccessError val:
|
||||||
|
:rtype: AccessError
|
||||||
|
"""
|
||||||
|
return cls('paper_access_denied', val)
|
||||||
|
|
||||||
|
def is_invalid_account_type(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``invalid_account_type``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'invalid_account_type'
|
||||||
|
|
||||||
|
def is_paper_access_denied(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``paper_access_denied``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'paper_access_denied'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def get_invalid_account_type(self):
|
||||||
|
"""
|
||||||
|
Current account type cannot access the resource.
|
||||||
|
|
||||||
|
Only call this if :meth:`is_invalid_account_type` is true.
|
||||||
|
|
||||||
|
:rtype: InvalidAccountTypeError
|
||||||
|
"""
|
||||||
|
if not self.is_invalid_account_type():
|
||||||
|
raise AttributeError("tag 'invalid_account_type' not set")
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def get_paper_access_denied(self):
|
||||||
|
"""
|
||||||
|
Current account cannot access Paper.
|
||||||
|
|
||||||
|
Only call this if :meth:`is_paper_access_denied` is true.
|
||||||
|
|
||||||
|
:rtype: PaperAccessError
|
||||||
|
"""
|
||||||
|
if not self.is_paper_access_denied():
|
||||||
|
raise AttributeError("tag 'paper_access_denied' not set")
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(AccessError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'AccessError(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
AccessError_validator = bv.Union(AccessError)
|
||||||
|
|
||||||
|
class AuthError(bb.Union):
|
||||||
|
"""
|
||||||
|
Errors occurred during authentication.
|
||||||
|
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar auth.AuthError.invalid_access_token: The access token is invalid.
|
||||||
|
:ivar auth.AuthError.invalid_select_user: The user specified in
|
||||||
|
'Dropbox-API-Select-User' is no longer on the team.
|
||||||
|
:ivar auth.AuthError.invalid_select_admin: The user specified in
|
||||||
|
'Dropbox-API-Select-Admin' is not a Dropbox Business team admin.
|
||||||
|
:ivar auth.AuthError.user_suspended: The user has been suspended.
|
||||||
|
:ivar auth.AuthError.expired_access_token: The access token has expired.
|
||||||
|
:ivar TokenScopeError AuthError.missing_scope: The access token does not
|
||||||
|
have the required scope to access the route.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
invalid_access_token = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
invalid_select_user = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
invalid_select_admin = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
user_suspended = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
expired_access_token = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def missing_scope(cls, val):
|
||||||
|
"""
|
||||||
|
Create an instance of this class set to the ``missing_scope`` tag with
|
||||||
|
value ``val``.
|
||||||
|
|
||||||
|
:param TokenScopeError val:
|
||||||
|
:rtype: AuthError
|
||||||
|
"""
|
||||||
|
return cls('missing_scope', val)
|
||||||
|
|
||||||
|
def is_invalid_access_token(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``invalid_access_token``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'invalid_access_token'
|
||||||
|
|
||||||
|
def is_invalid_select_user(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``invalid_select_user``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'invalid_select_user'
|
||||||
|
|
||||||
|
def is_invalid_select_admin(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``invalid_select_admin``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'invalid_select_admin'
|
||||||
|
|
||||||
|
def is_user_suspended(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``user_suspended``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'user_suspended'
|
||||||
|
|
||||||
|
def is_expired_access_token(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``expired_access_token``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'expired_access_token'
|
||||||
|
|
||||||
|
def is_missing_scope(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``missing_scope``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'missing_scope'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def get_missing_scope(self):
|
||||||
|
"""
|
||||||
|
The access token does not have the required scope to access the route.
|
||||||
|
|
||||||
|
Only call this if :meth:`is_missing_scope` is true.
|
||||||
|
|
||||||
|
:rtype: TokenScopeError
|
||||||
|
"""
|
||||||
|
if not self.is_missing_scope():
|
||||||
|
raise AttributeError("tag 'missing_scope' not set")
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(AuthError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'AuthError(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
AuthError_validator = bv.Union(AuthError)
|
||||||
|
|
||||||
|
class InvalidAccountTypeError(bb.Union):
|
||||||
|
"""
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar auth.InvalidAccountTypeError.endpoint: Current account type doesn't
|
||||||
|
have permission to access this route endpoint.
|
||||||
|
:ivar auth.InvalidAccountTypeError.feature: Current account type doesn't
|
||||||
|
have permission to access this feature.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
endpoint = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
feature = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_endpoint(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``endpoint``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'endpoint'
|
||||||
|
|
||||||
|
def is_feature(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``feature``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'feature'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(InvalidAccountTypeError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'InvalidAccountTypeError(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
InvalidAccountTypeError_validator = bv.Union(InvalidAccountTypeError)
|
||||||
|
|
||||||
|
class PaperAccessError(bb.Union):
|
||||||
|
"""
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar auth.PaperAccessError.paper_disabled: Paper is disabled.
|
||||||
|
:ivar auth.PaperAccessError.not_paper_user: The provided user has not used
|
||||||
|
Paper yet.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
paper_disabled = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
not_paper_user = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_paper_disabled(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``paper_disabled``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'paper_disabled'
|
||||||
|
|
||||||
|
def is_not_paper_user(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``not_paper_user``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'not_paper_user'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(PaperAccessError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'PaperAccessError(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
PaperAccessError_validator = bv.Union(PaperAccessError)
|
||||||
|
|
||||||
|
class RateLimitError(bb.Struct):
|
||||||
|
"""
|
||||||
|
Error occurred because the app is being rate limited.
|
||||||
|
|
||||||
|
:ivar auth.RateLimitError.reason: The reason why the app is being rate
|
||||||
|
limited.
|
||||||
|
:ivar auth.RateLimitError.retry_after: The number of seconds that the app
|
||||||
|
should wait before making another request.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_reason_value',
|
||||||
|
'_reason_present',
|
||||||
|
'_retry_after_value',
|
||||||
|
'_retry_after_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
reason=None,
|
||||||
|
retry_after=None):
|
||||||
|
self._reason_value = None
|
||||||
|
self._reason_present = False
|
||||||
|
self._retry_after_value = None
|
||||||
|
self._retry_after_present = False
|
||||||
|
if reason is not None:
|
||||||
|
self.reason = reason
|
||||||
|
if retry_after is not None:
|
||||||
|
self.retry_after = retry_after
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reason(self):
|
||||||
|
"""
|
||||||
|
The reason why the app is being rate limited.
|
||||||
|
|
||||||
|
:rtype: RateLimitReason
|
||||||
|
"""
|
||||||
|
if self._reason_present:
|
||||||
|
return self._reason_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'reason'")
|
||||||
|
|
||||||
|
@reason.setter
|
||||||
|
def reason(self, val):
|
||||||
|
self._reason_validator.validate_type_only(val)
|
||||||
|
self._reason_value = val
|
||||||
|
self._reason_present = True
|
||||||
|
|
||||||
|
@reason.deleter
|
||||||
|
def reason(self):
|
||||||
|
self._reason_value = None
|
||||||
|
self._reason_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def retry_after(self):
|
||||||
|
"""
|
||||||
|
The number of seconds that the app should wait before making another
|
||||||
|
request.
|
||||||
|
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
if self._retry_after_present:
|
||||||
|
return self._retry_after_value
|
||||||
|
else:
|
||||||
|
return 1
|
||||||
|
|
||||||
|
@retry_after.setter
|
||||||
|
def retry_after(self, val):
|
||||||
|
val = self._retry_after_validator.validate(val)
|
||||||
|
self._retry_after_value = val
|
||||||
|
self._retry_after_present = True
|
||||||
|
|
||||||
|
@retry_after.deleter
|
||||||
|
def retry_after(self):
|
||||||
|
self._retry_after_value = None
|
||||||
|
self._retry_after_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(RateLimitError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'RateLimitError(reason={!r}, retry_after={!r})'.format(
|
||||||
|
self._reason_value,
|
||||||
|
self._retry_after_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
RateLimitError_validator = bv.Struct(RateLimitError)
|
||||||
|
|
||||||
|
class RateLimitReason(bb.Union):
|
||||||
|
"""
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar auth.RateLimitReason.too_many_requests: You are making too many
|
||||||
|
requests in the past few minutes.
|
||||||
|
:ivar auth.RateLimitReason.too_many_write_operations: There are currently
|
||||||
|
too many write operations happening in the user's Dropbox.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
too_many_requests = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
too_many_write_operations = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_too_many_requests(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``too_many_requests``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'too_many_requests'
|
||||||
|
|
||||||
|
def is_too_many_write_operations(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``too_many_write_operations``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'too_many_write_operations'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(RateLimitReason, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'RateLimitReason(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
RateLimitReason_validator = bv.Union(RateLimitReason)
|
||||||
|
|
||||||
|
class TokenFromOAuth1Arg(bb.Struct):
|
||||||
|
"""
|
||||||
|
:ivar auth.TokenFromOAuth1Arg.oauth1_token: The supplied OAuth 1.0 access
|
||||||
|
token.
|
||||||
|
:ivar auth.TokenFromOAuth1Arg.oauth1_token_secret: The token secret
|
||||||
|
associated with the supplied access token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_oauth1_token_value',
|
||||||
|
'_oauth1_token_present',
|
||||||
|
'_oauth1_token_secret_value',
|
||||||
|
'_oauth1_token_secret_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
oauth1_token=None,
|
||||||
|
oauth1_token_secret=None):
|
||||||
|
self._oauth1_token_value = None
|
||||||
|
self._oauth1_token_present = False
|
||||||
|
self._oauth1_token_secret_value = None
|
||||||
|
self._oauth1_token_secret_present = False
|
||||||
|
if oauth1_token is not None:
|
||||||
|
self.oauth1_token = oauth1_token
|
||||||
|
if oauth1_token_secret is not None:
|
||||||
|
self.oauth1_token_secret = oauth1_token_secret
|
||||||
|
|
||||||
|
@property
|
||||||
|
def oauth1_token(self):
|
||||||
|
"""
|
||||||
|
The supplied OAuth 1.0 access token.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._oauth1_token_present:
|
||||||
|
return self._oauth1_token_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'oauth1_token'")
|
||||||
|
|
||||||
|
@oauth1_token.setter
|
||||||
|
def oauth1_token(self, val):
|
||||||
|
val = self._oauth1_token_validator.validate(val)
|
||||||
|
self._oauth1_token_value = val
|
||||||
|
self._oauth1_token_present = True
|
||||||
|
|
||||||
|
@oauth1_token.deleter
|
||||||
|
def oauth1_token(self):
|
||||||
|
self._oauth1_token_value = None
|
||||||
|
self._oauth1_token_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def oauth1_token_secret(self):
|
||||||
|
"""
|
||||||
|
The token secret associated with the supplied access token.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._oauth1_token_secret_present:
|
||||||
|
return self._oauth1_token_secret_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'oauth1_token_secret'")
|
||||||
|
|
||||||
|
@oauth1_token_secret.setter
|
||||||
|
def oauth1_token_secret(self, val):
|
||||||
|
val = self._oauth1_token_secret_validator.validate(val)
|
||||||
|
self._oauth1_token_secret_value = val
|
||||||
|
self._oauth1_token_secret_present = True
|
||||||
|
|
||||||
|
@oauth1_token_secret.deleter
|
||||||
|
def oauth1_token_secret(self):
|
||||||
|
self._oauth1_token_secret_value = None
|
||||||
|
self._oauth1_token_secret_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(TokenFromOAuth1Arg, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'TokenFromOAuth1Arg(oauth1_token={!r}, oauth1_token_secret={!r})'.format(
|
||||||
|
self._oauth1_token_value,
|
||||||
|
self._oauth1_token_secret_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
TokenFromOAuth1Arg_validator = bv.Struct(TokenFromOAuth1Arg)
|
||||||
|
|
||||||
|
class TokenFromOAuth1Error(bb.Union):
|
||||||
|
"""
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar auth.TokenFromOAuth1Error.invalid_oauth1_token_info: Part or all of
|
||||||
|
the OAuth 1.0 access token info is invalid.
|
||||||
|
:ivar auth.TokenFromOAuth1Error.app_id_mismatch: The authorized app does not
|
||||||
|
match the app associated with the supplied access token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
invalid_oauth1_token_info = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
app_id_mismatch = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_invalid_oauth1_token_info(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``invalid_oauth1_token_info``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'invalid_oauth1_token_info'
|
||||||
|
|
||||||
|
def is_app_id_mismatch(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``app_id_mismatch``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'app_id_mismatch'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(TokenFromOAuth1Error, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'TokenFromOAuth1Error(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
TokenFromOAuth1Error_validator = bv.Union(TokenFromOAuth1Error)
|
||||||
|
|
||||||
|
class TokenFromOAuth1Result(bb.Struct):
|
||||||
|
"""
|
||||||
|
:ivar auth.TokenFromOAuth1Result.oauth2_token: The OAuth 2.0 token generated
|
||||||
|
from the supplied OAuth 1.0 token.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_oauth2_token_value',
|
||||||
|
'_oauth2_token_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
oauth2_token=None):
|
||||||
|
self._oauth2_token_value = None
|
||||||
|
self._oauth2_token_present = False
|
||||||
|
if oauth2_token is not None:
|
||||||
|
self.oauth2_token = oauth2_token
|
||||||
|
|
||||||
|
@property
|
||||||
|
def oauth2_token(self):
|
||||||
|
"""
|
||||||
|
The OAuth 2.0 token generated from the supplied OAuth 1.0 token.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._oauth2_token_present:
|
||||||
|
return self._oauth2_token_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'oauth2_token'")
|
||||||
|
|
||||||
|
@oauth2_token.setter
|
||||||
|
def oauth2_token(self, val):
|
||||||
|
val = self._oauth2_token_validator.validate(val)
|
||||||
|
self._oauth2_token_value = val
|
||||||
|
self._oauth2_token_present = True
|
||||||
|
|
||||||
|
@oauth2_token.deleter
|
||||||
|
def oauth2_token(self):
|
||||||
|
self._oauth2_token_value = None
|
||||||
|
self._oauth2_token_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(TokenFromOAuth1Result, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'TokenFromOAuth1Result(oauth2_token={!r})'.format(
|
||||||
|
self._oauth2_token_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
TokenFromOAuth1Result_validator = bv.Struct(TokenFromOAuth1Result)
|
||||||
|
|
||||||
|
class TokenScopeError(bb.Struct):
|
||||||
|
"""
|
||||||
|
:ivar auth.TokenScopeError.required_scope: The required scope to access the
|
||||||
|
route.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_required_scope_value',
|
||||||
|
'_required_scope_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
required_scope=None):
|
||||||
|
self._required_scope_value = None
|
||||||
|
self._required_scope_present = False
|
||||||
|
if required_scope is not None:
|
||||||
|
self.required_scope = required_scope
|
||||||
|
|
||||||
|
@property
|
||||||
|
def required_scope(self):
|
||||||
|
"""
|
||||||
|
The required scope to access the route.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._required_scope_present:
|
||||||
|
return self._required_scope_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'required_scope'")
|
||||||
|
|
||||||
|
@required_scope.setter
|
||||||
|
def required_scope(self, val):
|
||||||
|
val = self._required_scope_validator.validate(val)
|
||||||
|
self._required_scope_value = val
|
||||||
|
self._required_scope_present = True
|
||||||
|
|
||||||
|
@required_scope.deleter
|
||||||
|
def required_scope(self):
|
||||||
|
self._required_scope_value = None
|
||||||
|
self._required_scope_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(TokenScopeError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'TokenScopeError(required_scope={!r})'.format(
|
||||||
|
self._required_scope_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
TokenScopeError_validator = bv.Struct(TokenScopeError)
|
||||||
|
|
||||||
|
AccessError._invalid_account_type_validator = InvalidAccountTypeError_validator
|
||||||
|
AccessError._paper_access_denied_validator = PaperAccessError_validator
|
||||||
|
AccessError._other_validator = bv.Void()
|
||||||
|
AccessError._tagmap = {
|
||||||
|
'invalid_account_type': AccessError._invalid_account_type_validator,
|
||||||
|
'paper_access_denied': AccessError._paper_access_denied_validator,
|
||||||
|
'other': AccessError._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
AccessError.other = AccessError('other')
|
||||||
|
|
||||||
|
AuthError._invalid_access_token_validator = bv.Void()
|
||||||
|
AuthError._invalid_select_user_validator = bv.Void()
|
||||||
|
AuthError._invalid_select_admin_validator = bv.Void()
|
||||||
|
AuthError._user_suspended_validator = bv.Void()
|
||||||
|
AuthError._expired_access_token_validator = bv.Void()
|
||||||
|
AuthError._missing_scope_validator = TokenScopeError_validator
|
||||||
|
AuthError._other_validator = bv.Void()
|
||||||
|
AuthError._tagmap = {
|
||||||
|
'invalid_access_token': AuthError._invalid_access_token_validator,
|
||||||
|
'invalid_select_user': AuthError._invalid_select_user_validator,
|
||||||
|
'invalid_select_admin': AuthError._invalid_select_admin_validator,
|
||||||
|
'user_suspended': AuthError._user_suspended_validator,
|
||||||
|
'expired_access_token': AuthError._expired_access_token_validator,
|
||||||
|
'missing_scope': AuthError._missing_scope_validator,
|
||||||
|
'other': AuthError._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
AuthError.invalid_access_token = AuthError('invalid_access_token')
|
||||||
|
AuthError.invalid_select_user = AuthError('invalid_select_user')
|
||||||
|
AuthError.invalid_select_admin = AuthError('invalid_select_admin')
|
||||||
|
AuthError.user_suspended = AuthError('user_suspended')
|
||||||
|
AuthError.expired_access_token = AuthError('expired_access_token')
|
||||||
|
AuthError.other = AuthError('other')
|
||||||
|
|
||||||
|
InvalidAccountTypeError._endpoint_validator = bv.Void()
|
||||||
|
InvalidAccountTypeError._feature_validator = bv.Void()
|
||||||
|
InvalidAccountTypeError._other_validator = bv.Void()
|
||||||
|
InvalidAccountTypeError._tagmap = {
|
||||||
|
'endpoint': InvalidAccountTypeError._endpoint_validator,
|
||||||
|
'feature': InvalidAccountTypeError._feature_validator,
|
||||||
|
'other': InvalidAccountTypeError._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
InvalidAccountTypeError.endpoint = InvalidAccountTypeError('endpoint')
|
||||||
|
InvalidAccountTypeError.feature = InvalidAccountTypeError('feature')
|
||||||
|
InvalidAccountTypeError.other = InvalidAccountTypeError('other')
|
||||||
|
|
||||||
|
PaperAccessError._paper_disabled_validator = bv.Void()
|
||||||
|
PaperAccessError._not_paper_user_validator = bv.Void()
|
||||||
|
PaperAccessError._other_validator = bv.Void()
|
||||||
|
PaperAccessError._tagmap = {
|
||||||
|
'paper_disabled': PaperAccessError._paper_disabled_validator,
|
||||||
|
'not_paper_user': PaperAccessError._not_paper_user_validator,
|
||||||
|
'other': PaperAccessError._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
PaperAccessError.paper_disabled = PaperAccessError('paper_disabled')
|
||||||
|
PaperAccessError.not_paper_user = PaperAccessError('not_paper_user')
|
||||||
|
PaperAccessError.other = PaperAccessError('other')
|
||||||
|
|
||||||
|
RateLimitError._reason_validator = RateLimitReason_validator
|
||||||
|
RateLimitError._retry_after_validator = bv.UInt64()
|
||||||
|
RateLimitError._all_field_names_ = set([
|
||||||
|
'reason',
|
||||||
|
'retry_after',
|
||||||
|
])
|
||||||
|
RateLimitError._all_fields_ = [
|
||||||
|
('reason', RateLimitError._reason_validator),
|
||||||
|
('retry_after', RateLimitError._retry_after_validator),
|
||||||
|
]
|
||||||
|
|
||||||
|
RateLimitReason._too_many_requests_validator = bv.Void()
|
||||||
|
RateLimitReason._too_many_write_operations_validator = bv.Void()
|
||||||
|
RateLimitReason._other_validator = bv.Void()
|
||||||
|
RateLimitReason._tagmap = {
|
||||||
|
'too_many_requests': RateLimitReason._too_many_requests_validator,
|
||||||
|
'too_many_write_operations': RateLimitReason._too_many_write_operations_validator,
|
||||||
|
'other': RateLimitReason._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
RateLimitReason.too_many_requests = RateLimitReason('too_many_requests')
|
||||||
|
RateLimitReason.too_many_write_operations = RateLimitReason('too_many_write_operations')
|
||||||
|
RateLimitReason.other = RateLimitReason('other')
|
||||||
|
|
||||||
|
TokenFromOAuth1Arg._oauth1_token_validator = bv.String(min_length=1)
|
||||||
|
TokenFromOAuth1Arg._oauth1_token_secret_validator = bv.String(min_length=1)
|
||||||
|
TokenFromOAuth1Arg._all_field_names_ = set([
|
||||||
|
'oauth1_token',
|
||||||
|
'oauth1_token_secret',
|
||||||
|
])
|
||||||
|
TokenFromOAuth1Arg._all_fields_ = [
|
||||||
|
('oauth1_token', TokenFromOAuth1Arg._oauth1_token_validator),
|
||||||
|
('oauth1_token_secret', TokenFromOAuth1Arg._oauth1_token_secret_validator),
|
||||||
|
]
|
||||||
|
|
||||||
|
TokenFromOAuth1Error._invalid_oauth1_token_info_validator = bv.Void()
|
||||||
|
TokenFromOAuth1Error._app_id_mismatch_validator = bv.Void()
|
||||||
|
TokenFromOAuth1Error._other_validator = bv.Void()
|
||||||
|
TokenFromOAuth1Error._tagmap = {
|
||||||
|
'invalid_oauth1_token_info': TokenFromOAuth1Error._invalid_oauth1_token_info_validator,
|
||||||
|
'app_id_mismatch': TokenFromOAuth1Error._app_id_mismatch_validator,
|
||||||
|
'other': TokenFromOAuth1Error._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
TokenFromOAuth1Error.invalid_oauth1_token_info = TokenFromOAuth1Error('invalid_oauth1_token_info')
|
||||||
|
TokenFromOAuth1Error.app_id_mismatch = TokenFromOAuth1Error('app_id_mismatch')
|
||||||
|
TokenFromOAuth1Error.other = TokenFromOAuth1Error('other')
|
||||||
|
|
||||||
|
TokenFromOAuth1Result._oauth2_token_validator = bv.String(min_length=1)
|
||||||
|
TokenFromOAuth1Result._all_field_names_ = set(['oauth2_token'])
|
||||||
|
TokenFromOAuth1Result._all_fields_ = [('oauth2_token', TokenFromOAuth1Result._oauth2_token_validator)]
|
||||||
|
|
||||||
|
TokenScopeError._required_scope_validator = bv.String()
|
||||||
|
TokenScopeError._all_field_names_ = set(['required_scope'])
|
||||||
|
TokenScopeError._all_fields_ = [('required_scope', TokenScopeError._required_scope_validator)]
|
||||||
|
|
||||||
|
token_from_oauth1 = bb.Route(
|
||||||
|
'token/from_oauth1',
|
||||||
|
1,
|
||||||
|
False,
|
||||||
|
TokenFromOAuth1Arg_validator,
|
||||||
|
TokenFromOAuth1Result_validator,
|
||||||
|
TokenFromOAuth1Error_validator,
|
||||||
|
{'host': u'api',
|
||||||
|
'style': u'rpc'},
|
||||||
|
)
|
||||||
|
token_revoke = bb.Route(
|
||||||
|
'token/revoke',
|
||||||
|
1,
|
||||||
|
False,
|
||||||
|
bv.Void(),
|
||||||
|
bv.Void(),
|
||||||
|
bv.Void(),
|
||||||
|
{'host': u'api',
|
||||||
|
'style': u'rpc'},
|
||||||
|
)
|
||||||
|
|
||||||
|
ROUTES = {
|
||||||
|
'token/from_oauth1': token_from_oauth1,
|
||||||
|
'token/revoke': token_revoke,
|
||||||
|
}
|
||||||
|
|
4526
resources/lib/dropbox/base.py
Normal file
4526
resources/lib/dropbox/base.py
Normal file
File diff suppressed because it is too large
Load Diff
1999
resources/lib/dropbox/base_team.py
Normal file
1999
resources/lib/dropbox/base_team.py
Normal file
File diff suppressed because it is too large
Load Diff
458
resources/lib/dropbox/common.py
Normal file
458
resources/lib/dropbox/common.py
Normal file
@ -0,0 +1,458 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Auto-generated by Stone, do not modify.
|
||||||
|
# @generated
|
||||||
|
# flake8: noqa
|
||||||
|
# pylint: skip-file
|
||||||
|
try:
|
||||||
|
from . import stone_validators as bv
|
||||||
|
from . import stone_base as bb
|
||||||
|
except (ImportError, SystemError, ValueError):
|
||||||
|
# Catch errors raised when importing a relative module when not in a package.
|
||||||
|
# This makes testing this file directly (outside of a package) easier.
|
||||||
|
import stone_validators as bv
|
||||||
|
import stone_base as bb
|
||||||
|
|
||||||
|
class PathRoot(bb.Union):
|
||||||
|
"""
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar common.PathRoot.home: Paths are relative to the authenticating user's
|
||||||
|
home namespace, whether or not that user belongs to a team.
|
||||||
|
:ivar str common.PathRoot.root: Paths are relative to the authenticating
|
||||||
|
user's root namespace (This results in
|
||||||
|
:field:`PathRootError.invalid_root` if the user's root namespace has
|
||||||
|
changed.).
|
||||||
|
:ivar str common.PathRoot.namespace_id: Paths are relative to given
|
||||||
|
namespace id (This results in :field:`PathRootError.no_permission` if
|
||||||
|
you don't have access to this namespace.).
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
home = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def root(cls, val):
|
||||||
|
"""
|
||||||
|
Create an instance of this class set to the ``root`` tag with value
|
||||||
|
``val``.
|
||||||
|
|
||||||
|
:param str val:
|
||||||
|
:rtype: PathRoot
|
||||||
|
"""
|
||||||
|
return cls('root', val)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def namespace_id(cls, val):
|
||||||
|
"""
|
||||||
|
Create an instance of this class set to the ``namespace_id`` tag with
|
||||||
|
value ``val``.
|
||||||
|
|
||||||
|
:param str val:
|
||||||
|
:rtype: PathRoot
|
||||||
|
"""
|
||||||
|
return cls('namespace_id', val)
|
||||||
|
|
||||||
|
def is_home(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``home``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'home'
|
||||||
|
|
||||||
|
def is_root(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``root``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'root'
|
||||||
|
|
||||||
|
def is_namespace_id(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``namespace_id``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'namespace_id'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def get_root(self):
|
||||||
|
"""
|
||||||
|
Paths are relative to the authenticating user's root namespace (This
|
||||||
|
results in ``PathRootError.invalid_root`` if the user's root namespace
|
||||||
|
has changed.).
|
||||||
|
|
||||||
|
Only call this if :meth:`is_root` is true.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if not self.is_root():
|
||||||
|
raise AttributeError("tag 'root' not set")
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def get_namespace_id(self):
|
||||||
|
"""
|
||||||
|
Paths are relative to given namespace id (This results in
|
||||||
|
``PathRootError.no_permission`` if you don't have access to this
|
||||||
|
namespace.).
|
||||||
|
|
||||||
|
Only call this if :meth:`is_namespace_id` is true.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if not self.is_namespace_id():
|
||||||
|
raise AttributeError("tag 'namespace_id' not set")
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(PathRoot, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'PathRoot(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
PathRoot_validator = bv.Union(PathRoot)
|
||||||
|
|
||||||
|
class PathRootError(bb.Union):
|
||||||
|
"""
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar RootInfo PathRootError.invalid_root: The root namespace id in
|
||||||
|
Dropbox-API-Path-Root header is not valid. The value of this error is
|
||||||
|
use's latest root info.
|
||||||
|
:ivar common.PathRootError.no_permission: You don't have permission to
|
||||||
|
access the namespace id in Dropbox-API-Path-Root header.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
no_permission = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def invalid_root(cls, val):
|
||||||
|
"""
|
||||||
|
Create an instance of this class set to the ``invalid_root`` tag with
|
||||||
|
value ``val``.
|
||||||
|
|
||||||
|
:param RootInfo val:
|
||||||
|
:rtype: PathRootError
|
||||||
|
"""
|
||||||
|
return cls('invalid_root', val)
|
||||||
|
|
||||||
|
def is_invalid_root(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``invalid_root``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'invalid_root'
|
||||||
|
|
||||||
|
def is_no_permission(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``no_permission``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'no_permission'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def get_invalid_root(self):
|
||||||
|
"""
|
||||||
|
The root namespace id in Dropbox-API-Path-Root header is not valid. The
|
||||||
|
value of this error is use's latest root info.
|
||||||
|
|
||||||
|
Only call this if :meth:`is_invalid_root` is true.
|
||||||
|
|
||||||
|
:rtype: RootInfo
|
||||||
|
"""
|
||||||
|
if not self.is_invalid_root():
|
||||||
|
raise AttributeError("tag 'invalid_root' not set")
|
||||||
|
return self._value
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(PathRootError, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'PathRootError(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
PathRootError_validator = bv.Union(PathRootError)
|
||||||
|
|
||||||
|
class RootInfo(bb.Struct):
|
||||||
|
"""
|
||||||
|
Information about current user's root.
|
||||||
|
|
||||||
|
:ivar common.RootInfo.root_namespace_id: The namespace ID for user's root
|
||||||
|
namespace. It will be the namespace ID of the shared team root if the
|
||||||
|
user is member of a team with a separate team root. Otherwise it will be
|
||||||
|
same as ``RootInfo.home_namespace_id``.
|
||||||
|
:ivar common.RootInfo.home_namespace_id: The namespace ID for user's home
|
||||||
|
namespace.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_root_namespace_id_value',
|
||||||
|
'_root_namespace_id_present',
|
||||||
|
'_home_namespace_id_value',
|
||||||
|
'_home_namespace_id_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
root_namespace_id=None,
|
||||||
|
home_namespace_id=None):
|
||||||
|
self._root_namespace_id_value = None
|
||||||
|
self._root_namespace_id_present = False
|
||||||
|
self._home_namespace_id_value = None
|
||||||
|
self._home_namespace_id_present = False
|
||||||
|
if root_namespace_id is not None:
|
||||||
|
self.root_namespace_id = root_namespace_id
|
||||||
|
if home_namespace_id is not None:
|
||||||
|
self.home_namespace_id = home_namespace_id
|
||||||
|
|
||||||
|
@property
|
||||||
|
def root_namespace_id(self):
|
||||||
|
"""
|
||||||
|
The namespace ID for user's root namespace. It will be the namespace ID
|
||||||
|
of the shared team root if the user is member of a team with a separate
|
||||||
|
team root. Otherwise it will be same as ``RootInfo.home_namespace_id``.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._root_namespace_id_present:
|
||||||
|
return self._root_namespace_id_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'root_namespace_id'")
|
||||||
|
|
||||||
|
@root_namespace_id.setter
|
||||||
|
def root_namespace_id(self, val):
|
||||||
|
val = self._root_namespace_id_validator.validate(val)
|
||||||
|
self._root_namespace_id_value = val
|
||||||
|
self._root_namespace_id_present = True
|
||||||
|
|
||||||
|
@root_namespace_id.deleter
|
||||||
|
def root_namespace_id(self):
|
||||||
|
self._root_namespace_id_value = None
|
||||||
|
self._root_namespace_id_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def home_namespace_id(self):
|
||||||
|
"""
|
||||||
|
The namespace ID for user's home namespace.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._home_namespace_id_present:
|
||||||
|
return self._home_namespace_id_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'home_namespace_id'")
|
||||||
|
|
||||||
|
@home_namespace_id.setter
|
||||||
|
def home_namespace_id(self, val):
|
||||||
|
val = self._home_namespace_id_validator.validate(val)
|
||||||
|
self._home_namespace_id_value = val
|
||||||
|
self._home_namespace_id_present = True
|
||||||
|
|
||||||
|
@home_namespace_id.deleter
|
||||||
|
def home_namespace_id(self):
|
||||||
|
self._home_namespace_id_value = None
|
||||||
|
self._home_namespace_id_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(RootInfo, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'RootInfo(root_namespace_id={!r}, home_namespace_id={!r})'.format(
|
||||||
|
self._root_namespace_id_value,
|
||||||
|
self._home_namespace_id_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
RootInfo_validator = bv.StructTree(RootInfo)
|
||||||
|
|
||||||
|
class TeamRootInfo(RootInfo):
|
||||||
|
"""
|
||||||
|
Root info when user is member of a team with a separate root namespace ID.
|
||||||
|
|
||||||
|
:ivar common.TeamRootInfo.home_path: The path for user's home directory
|
||||||
|
under the shared team root.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_home_path_value',
|
||||||
|
'_home_path_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
root_namespace_id=None,
|
||||||
|
home_namespace_id=None,
|
||||||
|
home_path=None):
|
||||||
|
super(TeamRootInfo, self).__init__(root_namespace_id,
|
||||||
|
home_namespace_id)
|
||||||
|
self._home_path_value = None
|
||||||
|
self._home_path_present = False
|
||||||
|
if home_path is not None:
|
||||||
|
self.home_path = home_path
|
||||||
|
|
||||||
|
@property
|
||||||
|
def home_path(self):
|
||||||
|
"""
|
||||||
|
The path for user's home directory under the shared team root.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._home_path_present:
|
||||||
|
return self._home_path_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'home_path'")
|
||||||
|
|
||||||
|
@home_path.setter
|
||||||
|
def home_path(self, val):
|
||||||
|
val = self._home_path_validator.validate(val)
|
||||||
|
self._home_path_value = val
|
||||||
|
self._home_path_present = True
|
||||||
|
|
||||||
|
@home_path.deleter
|
||||||
|
def home_path(self):
|
||||||
|
self._home_path_value = None
|
||||||
|
self._home_path_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(TeamRootInfo, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'TeamRootInfo(root_namespace_id={!r}, home_namespace_id={!r}, home_path={!r})'.format(
|
||||||
|
self._root_namespace_id_value,
|
||||||
|
self._home_namespace_id_value,
|
||||||
|
self._home_path_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
TeamRootInfo_validator = bv.Struct(TeamRootInfo)
|
||||||
|
|
||||||
|
class UserRootInfo(RootInfo):
|
||||||
|
"""
|
||||||
|
Root info when user is not member of a team or the user is a member of a
|
||||||
|
team and the team does not have a separate root namespace.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
root_namespace_id=None,
|
||||||
|
home_namespace_id=None):
|
||||||
|
super(UserRootInfo, self).__init__(root_namespace_id,
|
||||||
|
home_namespace_id)
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(UserRootInfo, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'UserRootInfo(root_namespace_id={!r}, home_namespace_id={!r})'.format(
|
||||||
|
self._root_namespace_id_value,
|
||||||
|
self._home_namespace_id_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
UserRootInfo_validator = bv.Struct(UserRootInfo)
|
||||||
|
|
||||||
|
Date_validator = bv.Timestamp(u'%Y-%m-%d')
|
||||||
|
DisplayName_validator = bv.String(min_length=1, pattern=u'[^/:?*<>"|]*')
|
||||||
|
DisplayNameLegacy_validator = bv.String()
|
||||||
|
DropboxTimestamp_validator = bv.Timestamp(u'%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
EmailAddress_validator = bv.String(max_length=255, pattern=u"^['&A-Za-z0-9._%+-]+@[A-Za-z0-9-][A-Za-z0-9.-]*\\.[A-Za-z]{2,15}$")
|
||||||
|
# A ISO639-1 code.
|
||||||
|
LanguageCode_validator = bv.String(min_length=2)
|
||||||
|
NamePart_validator = bv.String(min_length=1, max_length=100, pattern=u'[^/:?*<>"|]*')
|
||||||
|
NamespaceId_validator = bv.String(pattern=u'[-_0-9a-zA-Z:]+')
|
||||||
|
OptionalNamePart_validator = bv.String(max_length=100, pattern=u'[^/:?*<>"|]*')
|
||||||
|
SessionId_validator = bv.String()
|
||||||
|
SharedFolderId_validator = NamespaceId_validator
|
||||||
|
PathRoot._home_validator = bv.Void()
|
||||||
|
PathRoot._root_validator = NamespaceId_validator
|
||||||
|
PathRoot._namespace_id_validator = NamespaceId_validator
|
||||||
|
PathRoot._other_validator = bv.Void()
|
||||||
|
PathRoot._tagmap = {
|
||||||
|
'home': PathRoot._home_validator,
|
||||||
|
'root': PathRoot._root_validator,
|
||||||
|
'namespace_id': PathRoot._namespace_id_validator,
|
||||||
|
'other': PathRoot._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
PathRoot.home = PathRoot('home')
|
||||||
|
PathRoot.other = PathRoot('other')
|
||||||
|
|
||||||
|
PathRootError._invalid_root_validator = RootInfo_validator
|
||||||
|
PathRootError._no_permission_validator = bv.Void()
|
||||||
|
PathRootError._other_validator = bv.Void()
|
||||||
|
PathRootError._tagmap = {
|
||||||
|
'invalid_root': PathRootError._invalid_root_validator,
|
||||||
|
'no_permission': PathRootError._no_permission_validator,
|
||||||
|
'other': PathRootError._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
PathRootError.no_permission = PathRootError('no_permission')
|
||||||
|
PathRootError.other = PathRootError('other')
|
||||||
|
|
||||||
|
RootInfo._root_namespace_id_validator = NamespaceId_validator
|
||||||
|
RootInfo._home_namespace_id_validator = NamespaceId_validator
|
||||||
|
RootInfo._field_names_ = set([
|
||||||
|
'root_namespace_id',
|
||||||
|
'home_namespace_id',
|
||||||
|
])
|
||||||
|
RootInfo._all_field_names_ = RootInfo._field_names_
|
||||||
|
RootInfo._fields_ = [
|
||||||
|
('root_namespace_id', RootInfo._root_namespace_id_validator),
|
||||||
|
('home_namespace_id', RootInfo._home_namespace_id_validator),
|
||||||
|
]
|
||||||
|
RootInfo._all_fields_ = RootInfo._fields_
|
||||||
|
|
||||||
|
RootInfo._tag_to_subtype_ = {
|
||||||
|
(u'team',): TeamRootInfo_validator,
|
||||||
|
(u'user',): UserRootInfo_validator,
|
||||||
|
}
|
||||||
|
RootInfo._pytype_to_tag_and_subtype_ = {
|
||||||
|
TeamRootInfo: ((u'team',), TeamRootInfo_validator),
|
||||||
|
UserRootInfo: ((u'user',), UserRootInfo_validator),
|
||||||
|
}
|
||||||
|
RootInfo._is_catch_all_ = True
|
||||||
|
|
||||||
|
TeamRootInfo._home_path_validator = bv.String()
|
||||||
|
TeamRootInfo._field_names_ = set(['home_path'])
|
||||||
|
TeamRootInfo._all_field_names_ = RootInfo._all_field_names_.union(TeamRootInfo._field_names_)
|
||||||
|
TeamRootInfo._fields_ = [('home_path', TeamRootInfo._home_path_validator)]
|
||||||
|
TeamRootInfo._all_fields_ = RootInfo._all_fields_ + TeamRootInfo._fields_
|
||||||
|
|
||||||
|
UserRootInfo._field_names_ = set([])
|
||||||
|
UserRootInfo._all_field_names_ = RootInfo._all_field_names_.union(UserRootInfo._field_names_)
|
||||||
|
UserRootInfo._fields_ = []
|
||||||
|
UserRootInfo._all_fields_ = RootInfo._all_fields_ + UserRootInfo._fields_
|
||||||
|
|
||||||
|
ROUTES = {
|
||||||
|
}
|
||||||
|
|
609
resources/lib/dropbox/dropbox.py
Normal file
609
resources/lib/dropbox/dropbox.py
Normal file
@ -0,0 +1,609 @@
|
|||||||
|
__all__ = [
|
||||||
|
'Dropbox',
|
||||||
|
'DropboxTeam',
|
||||||
|
'create_session',
|
||||||
|
]
|
||||||
|
|
||||||
|
# This should always be 0.0.0 in master. Only update this after tagging
|
||||||
|
# before release.
|
||||||
|
__version__ = '9.4.0'
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import random
|
||||||
|
import time
|
||||||
|
|
||||||
|
import requests
|
||||||
|
import six
|
||||||
|
|
||||||
|
from . import files, stone_serializers
|
||||||
|
from .auth import (
|
||||||
|
AuthError_validator,
|
||||||
|
RateLimitError_validator,
|
||||||
|
)
|
||||||
|
from .common import (
|
||||||
|
PathRoot,
|
||||||
|
PathRoot_validator,
|
||||||
|
PathRootError_validator
|
||||||
|
)
|
||||||
|
from .base import DropboxBase
|
||||||
|
from .base_team import DropboxTeamBase
|
||||||
|
from .exceptions import (
|
||||||
|
ApiError,
|
||||||
|
AuthError,
|
||||||
|
BadInputError,
|
||||||
|
HttpError,
|
||||||
|
PathRootError,
|
||||||
|
InternalServerError,
|
||||||
|
RateLimitError,
|
||||||
|
)
|
||||||
|
from .session import (
|
||||||
|
API_HOST,
|
||||||
|
API_CONTENT_HOST,
|
||||||
|
API_NOTIFICATION_HOST,
|
||||||
|
HOST_API,
|
||||||
|
HOST_CONTENT,
|
||||||
|
HOST_NOTIFY,
|
||||||
|
pinned_session,
|
||||||
|
)
|
||||||
|
|
||||||
|
PATH_ROOT_HEADER = 'Dropbox-API-Path-Root'
|
||||||
|
HTTP_STATUS_INVALID_PATH_ROOT = 422
|
||||||
|
|
||||||
|
class RouteResult(object):
|
||||||
|
"""The successful result of a call to a route."""
|
||||||
|
|
||||||
|
def __init__(self, obj_result, http_resp=None):
|
||||||
|
"""
|
||||||
|
:param str obj_result: The result of a route not including the binary
|
||||||
|
payload portion, if one exists. Must be serialized JSON.
|
||||||
|
:param requests.models.Response http_resp: A raw HTTP response. It will
|
||||||
|
be used to stream the binary-body payload of the response.
|
||||||
|
"""
|
||||||
|
assert isinstance(obj_result, six.string_types), \
|
||||||
|
'obj_result: expected string, got %r' % type(obj_result)
|
||||||
|
if http_resp is not None:
|
||||||
|
assert isinstance(http_resp, requests.models.Response), \
|
||||||
|
'http_resp: expected requests.models.Response, got %r' % \
|
||||||
|
type(http_resp)
|
||||||
|
self.obj_result = obj_result
|
||||||
|
self.http_resp = http_resp
|
||||||
|
|
||||||
|
class RouteErrorResult(object):
|
||||||
|
"""The error result of a call to a route."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, obj_result):
|
||||||
|
"""
|
||||||
|
:param str request_id: A request_id can be shared with Dropbox Support
|
||||||
|
to pinpoint the exact request that returns an error.
|
||||||
|
:param str obj_result: The result of a route not including the binary
|
||||||
|
payload portion, if one exists.
|
||||||
|
"""
|
||||||
|
self.request_id = request_id
|
||||||
|
self.obj_result = obj_result
|
||||||
|
|
||||||
|
def create_session(max_connections=8, proxies=None):
|
||||||
|
"""
|
||||||
|
Creates a session object that can be used by multiple :class:`Dropbox` and
|
||||||
|
:class:`DropboxTeam` instances. This lets you share a connection pool
|
||||||
|
amongst them, as well as proxy parameters.
|
||||||
|
|
||||||
|
:param int max_connections: Maximum connection pool size.
|
||||||
|
:param dict proxies: See the `requests module
|
||||||
|
<http://docs.python-requests.org/en/latest/user/advanced/#proxies>`_
|
||||||
|
for more details.
|
||||||
|
:rtype: :class:`requests.sessions.Session`. `See the requests module
|
||||||
|
<http://docs.python-requests.org/en/latest/user/advanced/#session-objects>`_
|
||||||
|
for more details.
|
||||||
|
"""
|
||||||
|
# We only need as many pool_connections as we have unique hostnames.
|
||||||
|
session = pinned_session(pool_maxsize=max_connections)
|
||||||
|
if proxies:
|
||||||
|
session.proxies = proxies
|
||||||
|
return session
|
||||||
|
|
||||||
|
class _DropboxTransport(object):
|
||||||
|
"""
|
||||||
|
Responsible for implementing the wire protocol for making requests to the
|
||||||
|
Dropbox API.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_API_VERSION = '2'
|
||||||
|
|
||||||
|
# Download style means that the route argument goes in a Dropbox-API-Arg
|
||||||
|
# header, and the result comes back in a Dropbox-API-Result header. The
|
||||||
|
# HTTP response body contains a binary payload.
|
||||||
|
_ROUTE_STYLE_DOWNLOAD = 'download'
|
||||||
|
|
||||||
|
# Upload style means that the route argument goes in a Dropbox-API-Arg
|
||||||
|
# header. The HTTP request body contains a binary payload. The result
|
||||||
|
# comes back in a Dropbox-API-Result header.
|
||||||
|
_ROUTE_STYLE_UPLOAD = 'upload'
|
||||||
|
|
||||||
|
# RPC style means that the argument and result of a route are contained in
|
||||||
|
# the HTTP body.
|
||||||
|
_ROUTE_STYLE_RPC = 'rpc'
|
||||||
|
|
||||||
|
# This is the default longest time we'll block on receiving data from the server
|
||||||
|
_DEFAULT_TIMEOUT = 30
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
oauth2_access_token,
|
||||||
|
max_retries_on_error=4,
|
||||||
|
max_retries_on_rate_limit=None,
|
||||||
|
user_agent=None,
|
||||||
|
session=None,
|
||||||
|
headers=None,
|
||||||
|
timeout=_DEFAULT_TIMEOUT):
|
||||||
|
"""
|
||||||
|
:param str oauth2_access_token: OAuth2 access token for making client
|
||||||
|
requests.
|
||||||
|
|
||||||
|
:param int max_retries_on_error: On 5xx errors, the number of times to
|
||||||
|
retry.
|
||||||
|
:param Optional[int] max_retries_on_rate_limit: On 429 errors, the
|
||||||
|
number of times to retry. If `None`, always retries.
|
||||||
|
:param str user_agent: The user agent to use when making requests. This
|
||||||
|
helps us identify requests coming from your application. We
|
||||||
|
recommend you use the format "AppName/Version". If set, we append
|
||||||
|
"/OfficialDropboxPythonSDKv2/__version__" to the user_agent,
|
||||||
|
:param session: If not provided, a new session (connection pool) is
|
||||||
|
created. To share a session across multiple clients, use
|
||||||
|
:func:`create_session`.
|
||||||
|
:type session: :class:`requests.sessions.Session`
|
||||||
|
:param dict headers: Additional headers to add to requests.
|
||||||
|
:param Optional[float] timeout: Maximum duration in seconds that
|
||||||
|
client will wait for any single packet from the
|
||||||
|
server. After the timeout the client will give up on
|
||||||
|
connection. If `None`, client will wait forever. Defaults
|
||||||
|
to 30 seconds.
|
||||||
|
"""
|
||||||
|
assert len(oauth2_access_token) > 0, \
|
||||||
|
'OAuth2 access token cannot be empty.'
|
||||||
|
assert headers is None or isinstance(headers, dict), \
|
||||||
|
'Expected dict, got %r' % headers
|
||||||
|
self._oauth2_access_token = oauth2_access_token
|
||||||
|
|
||||||
|
self._max_retries_on_error = max_retries_on_error
|
||||||
|
self._max_retries_on_rate_limit = max_retries_on_rate_limit
|
||||||
|
if session:
|
||||||
|
assert isinstance(session, requests.sessions.Session), \
|
||||||
|
'Expected requests.sessions.Session, got %r' % session
|
||||||
|
self._session = session
|
||||||
|
else:
|
||||||
|
self._session = create_session()
|
||||||
|
self._headers = headers
|
||||||
|
|
||||||
|
base_user_agent = 'OfficialDropboxPythonSDKv2/' + __version__
|
||||||
|
if user_agent:
|
||||||
|
self._raw_user_agent = user_agent
|
||||||
|
self._user_agent = '{}/{}'.format(user_agent, base_user_agent)
|
||||||
|
else:
|
||||||
|
self._raw_user_agent = None
|
||||||
|
self._user_agent = base_user_agent
|
||||||
|
|
||||||
|
self._logger = logging.getLogger('dropbox')
|
||||||
|
|
||||||
|
self._host_map = {HOST_API: API_HOST,
|
||||||
|
HOST_CONTENT: API_CONTENT_HOST,
|
||||||
|
HOST_NOTIFY: API_NOTIFICATION_HOST}
|
||||||
|
|
||||||
|
self._timeout = timeout
|
||||||
|
|
||||||
|
def clone(
|
||||||
|
self,
|
||||||
|
oauth2_access_token=None,
|
||||||
|
max_retries_on_error=None,
|
||||||
|
max_retries_on_rate_limit=None,
|
||||||
|
user_agent=None,
|
||||||
|
session=None,
|
||||||
|
headers=None,
|
||||||
|
timeout=None):
|
||||||
|
"""
|
||||||
|
Creates a new copy of the Dropbox client with the same defaults unless modified by
|
||||||
|
arguments to clone()
|
||||||
|
|
||||||
|
See constructor for original parameter descriptions.
|
||||||
|
|
||||||
|
:return: New instance of Dropbox clent
|
||||||
|
:rtype: Dropbox
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self.__class__(
|
||||||
|
oauth2_access_token or self._oauth2_access_token,
|
||||||
|
max_retries_on_error or self._max_retries_on_error,
|
||||||
|
max_retries_on_rate_limit or self._max_retries_on_rate_limit,
|
||||||
|
user_agent or self._user_agent,
|
||||||
|
session or self._session,
|
||||||
|
headers or self._headers,
|
||||||
|
timeout or self._timeout
|
||||||
|
)
|
||||||
|
|
||||||
|
def request(self,
|
||||||
|
route,
|
||||||
|
namespace,
|
||||||
|
request_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=None):
|
||||||
|
"""
|
||||||
|
Makes a request to the Dropbox API and in the process validates that
|
||||||
|
the route argument and result are the expected data types. The
|
||||||
|
request_arg is converted to JSON based on the arg_data_type. Likewise,
|
||||||
|
the response is deserialized from JSON and converted to an object based
|
||||||
|
on the {result,error}_data_type.
|
||||||
|
|
||||||
|
:param host: The Dropbox API host to connect to.
|
||||||
|
:param route: The route to make the request to.
|
||||||
|
:type route: :class:`.datatypes.stone_base.Route`
|
||||||
|
:param request_arg: Argument for the route that conforms to the
|
||||||
|
validator specified by route.arg_type.
|
||||||
|
:param request_binary: String or file pointer representing the binary
|
||||||
|
payload. Use None if there is no binary payload.
|
||||||
|
:param Optional[float] timeout: Maximum duration in seconds
|
||||||
|
that client will wait for any single packet from the
|
||||||
|
server. After the timeout the client will give up on
|
||||||
|
connection. If `None`, will use default timeout set on
|
||||||
|
Dropbox object. Defaults to `None`.
|
||||||
|
:return: The route's result.
|
||||||
|
"""
|
||||||
|
host = route.attrs['host'] or 'api'
|
||||||
|
route_name = namespace + '/' + route.name
|
||||||
|
if route.version > 1:
|
||||||
|
route_name += '_v{}'.format(route.version)
|
||||||
|
route_style = route.attrs['style'] or 'rpc'
|
||||||
|
serialized_arg = stone_serializers.json_encode(route.arg_type,
|
||||||
|
request_arg)
|
||||||
|
|
||||||
|
if (timeout is None and
|
||||||
|
route == files.list_folder_longpoll):
|
||||||
|
# The client normally sends a timeout value to the
|
||||||
|
# longpoll route. The server will respond after
|
||||||
|
# <timeout> + random(0, 90) seconds. We increase the
|
||||||
|
# socket timeout to the longpoll timeout value plus 90
|
||||||
|
# seconds so that we don't cut the server response short
|
||||||
|
# due to a shorter socket timeout.
|
||||||
|
# NB: This is done here because base.py is auto-generated
|
||||||
|
timeout = request_arg.timeout + 90
|
||||||
|
|
||||||
|
res = self.request_json_string_with_retry(host,
|
||||||
|
route_name,
|
||||||
|
route_style,
|
||||||
|
serialized_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=timeout)
|
||||||
|
decoded_obj_result = json.loads(res.obj_result)
|
||||||
|
if isinstance(res, RouteResult):
|
||||||
|
returned_data_type = route.result_type
|
||||||
|
obj = decoded_obj_result
|
||||||
|
elif isinstance(res, RouteErrorResult):
|
||||||
|
returned_data_type = route.error_type
|
||||||
|
obj = decoded_obj_result['error']
|
||||||
|
user_message = decoded_obj_result.get('user_message')
|
||||||
|
user_message_text = user_message and user_message.get('text')
|
||||||
|
user_message_locale = user_message and user_message.get('locale')
|
||||||
|
else:
|
||||||
|
raise AssertionError('Expected RouteResult or RouteErrorResult, '
|
||||||
|
'but res is %s' % type(res))
|
||||||
|
|
||||||
|
deserialized_result = stone_serializers.json_compat_obj_decode(
|
||||||
|
returned_data_type, obj, strict=False)
|
||||||
|
|
||||||
|
if isinstance(res, RouteErrorResult):
|
||||||
|
raise ApiError(res.request_id,
|
||||||
|
deserialized_result,
|
||||||
|
user_message_text,
|
||||||
|
user_message_locale)
|
||||||
|
elif route_style == self._ROUTE_STYLE_DOWNLOAD:
|
||||||
|
return (deserialized_result, res.http_resp)
|
||||||
|
else:
|
||||||
|
return deserialized_result
|
||||||
|
|
||||||
|
def request_json_object(self,
|
||||||
|
host,
|
||||||
|
route_name,
|
||||||
|
route_style,
|
||||||
|
request_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=None):
|
||||||
|
"""
|
||||||
|
Makes a request to the Dropbox API, taking a JSON-serializable Python
|
||||||
|
object as an argument, and returning one as a response.
|
||||||
|
|
||||||
|
:param host: The Dropbox API host to connect to.
|
||||||
|
:param route_name: The name of the route to invoke.
|
||||||
|
:param route_style: The style of the route.
|
||||||
|
:param str request_arg: A JSON-serializable Python object representing
|
||||||
|
the argument for the route.
|
||||||
|
:param Optional[bytes] request_binary: Bytes representing the binary
|
||||||
|
payload. Use None if there is no binary payload.
|
||||||
|
:param Optional[float] timeout: Maximum duration in seconds
|
||||||
|
that client will wait for any single packet from the
|
||||||
|
server. After the timeout the client will give up on
|
||||||
|
connection. If `None`, will use default timeout set on
|
||||||
|
Dropbox object. Defaults to `None`.
|
||||||
|
:return: The route's result as a JSON-serializable Python object.
|
||||||
|
"""
|
||||||
|
serialized_arg = json.dumps(request_arg)
|
||||||
|
res = self.request_json_string_with_retry(host,
|
||||||
|
route_name,
|
||||||
|
route_style,
|
||||||
|
serialized_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=timeout)
|
||||||
|
# This can throw a ValueError if the result is not deserializable,
|
||||||
|
# but that would be completely unexpected.
|
||||||
|
deserialized_result = json.loads(res.obj_result)
|
||||||
|
if isinstance(res, RouteResult) and res.http_resp is not None:
|
||||||
|
return (deserialized_result, res.http_resp)
|
||||||
|
else:
|
||||||
|
return deserialized_result
|
||||||
|
|
||||||
|
def request_json_string_with_retry(self,
|
||||||
|
host,
|
||||||
|
route_name,
|
||||||
|
route_style,
|
||||||
|
request_json_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=None):
|
||||||
|
"""
|
||||||
|
See :meth:`request_json_object` for description of parameters.
|
||||||
|
|
||||||
|
:param request_json_arg: A string representing the serialized JSON
|
||||||
|
argument to the route.
|
||||||
|
"""
|
||||||
|
attempt = 0
|
||||||
|
rate_limit_errors = 0
|
||||||
|
while True:
|
||||||
|
self._logger.info('Request to %s', route_name)
|
||||||
|
try:
|
||||||
|
return self.request_json_string(host,
|
||||||
|
route_name,
|
||||||
|
route_style,
|
||||||
|
request_json_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=timeout)
|
||||||
|
except InternalServerError as e:
|
||||||
|
attempt += 1
|
||||||
|
if attempt <= self._max_retries_on_error:
|
||||||
|
# Use exponential backoff
|
||||||
|
backoff = 2**attempt * random.random()
|
||||||
|
self._logger.info(
|
||||||
|
'HttpError status_code=%s: Retrying in %.1f seconds',
|
||||||
|
e.status_code, backoff)
|
||||||
|
time.sleep(backoff)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
except RateLimitError as e:
|
||||||
|
rate_limit_errors += 1
|
||||||
|
if (self._max_retries_on_rate_limit is None or
|
||||||
|
self._max_retries_on_rate_limit >= rate_limit_errors):
|
||||||
|
# Set default backoff to 5 seconds.
|
||||||
|
backoff = e.backoff if e.backoff is not None else 5.0
|
||||||
|
self._logger.info(
|
||||||
|
'Ratelimit: Retrying in %.1f seconds.', backoff)
|
||||||
|
time.sleep(backoff)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
def request_json_string(self,
|
||||||
|
host,
|
||||||
|
func_name,
|
||||||
|
route_style,
|
||||||
|
request_json_arg,
|
||||||
|
request_binary,
|
||||||
|
timeout=None):
|
||||||
|
"""
|
||||||
|
See :meth:`request_json_string_with_retry` for description of
|
||||||
|
parameters.
|
||||||
|
"""
|
||||||
|
if host not in self._host_map:
|
||||||
|
raise ValueError('Unknown value for host: %r' % host)
|
||||||
|
|
||||||
|
if not isinstance(request_binary, (six.binary_type, type(None))):
|
||||||
|
# Disallow streams and file-like objects even though the underlying
|
||||||
|
# requests library supports them. This is to prevent incorrect
|
||||||
|
# behavior when a non-rewindable stream is read from, but the
|
||||||
|
# request fails and needs to be re-tried at a later time.
|
||||||
|
raise TypeError('expected request_binary as binary type, got %s' %
|
||||||
|
type(request_binary))
|
||||||
|
|
||||||
|
# Fully qualified hostname
|
||||||
|
fq_hostname = self._host_map[host]
|
||||||
|
url = self._get_route_url(fq_hostname, func_name)
|
||||||
|
|
||||||
|
headers = {'User-Agent': self._user_agent}
|
||||||
|
if host != HOST_NOTIFY:
|
||||||
|
headers['Authorization'] = 'Bearer %s' % self._oauth2_access_token
|
||||||
|
if self._headers:
|
||||||
|
headers.update(self._headers)
|
||||||
|
|
||||||
|
# The contents of the body of the HTTP request
|
||||||
|
body = None
|
||||||
|
# Whether the response should be streamed incrementally, or buffered
|
||||||
|
# entirely. If stream is True, the caller is responsible for closing
|
||||||
|
# the HTTP response.
|
||||||
|
stream = False
|
||||||
|
|
||||||
|
if route_style == self._ROUTE_STYLE_RPC:
|
||||||
|
headers['Content-Type'] = 'application/json'
|
||||||
|
body = request_json_arg
|
||||||
|
elif route_style == self._ROUTE_STYLE_DOWNLOAD:
|
||||||
|
headers['Dropbox-API-Arg'] = request_json_arg
|
||||||
|
stream = True
|
||||||
|
elif route_style == self._ROUTE_STYLE_UPLOAD:
|
||||||
|
headers['Content-Type'] = 'application/octet-stream'
|
||||||
|
headers['Dropbox-API-Arg'] = request_json_arg
|
||||||
|
body = request_binary
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown operation style: %r' % route_style)
|
||||||
|
|
||||||
|
if timeout is None:
|
||||||
|
timeout = self._timeout
|
||||||
|
|
||||||
|
r = self._session.post(url,
|
||||||
|
headers=headers,
|
||||||
|
data=body,
|
||||||
|
stream=stream,
|
||||||
|
verify=True,
|
||||||
|
timeout=timeout,
|
||||||
|
)
|
||||||
|
|
||||||
|
request_id = r.headers.get('x-dropbox-request-id')
|
||||||
|
if r.status_code >= 500:
|
||||||
|
raise InternalServerError(request_id, r.status_code, r.text)
|
||||||
|
elif r.status_code == 400:
|
||||||
|
raise BadInputError(request_id, r.text)
|
||||||
|
elif r.status_code == 401:
|
||||||
|
assert r.headers.get('content-type') == 'application/json', (
|
||||||
|
'Expected content-type to be application/json, got %r' %
|
||||||
|
r.headers.get('content-type'))
|
||||||
|
err = stone_serializers.json_compat_obj_decode(
|
||||||
|
AuthError_validator, r.json()['error'])
|
||||||
|
raise AuthError(request_id, err)
|
||||||
|
elif r.status_code == HTTP_STATUS_INVALID_PATH_ROOT:
|
||||||
|
err = stone_serializers.json_compat_obj_decode(
|
||||||
|
PathRootError_validator, r.json()['error'])
|
||||||
|
raise PathRootError(request_id, err)
|
||||||
|
elif r.status_code == 429:
|
||||||
|
err = None
|
||||||
|
if r.headers.get('content-type') == 'application/json':
|
||||||
|
err = stone_serializers.json_compat_obj_decode(
|
||||||
|
RateLimitError_validator, r.json()['error'])
|
||||||
|
retry_after = err.retry_after
|
||||||
|
else:
|
||||||
|
retry_after_str = r.headers.get('retry-after')
|
||||||
|
if retry_after_str is not None:
|
||||||
|
retry_after = int(retry_after_str)
|
||||||
|
else:
|
||||||
|
retry_after = None
|
||||||
|
raise RateLimitError(request_id, err, retry_after)
|
||||||
|
elif 200 <= r.status_code <= 299:
|
||||||
|
if route_style == self._ROUTE_STYLE_DOWNLOAD:
|
||||||
|
raw_resp = r.headers['dropbox-api-result']
|
||||||
|
else:
|
||||||
|
assert r.headers.get('content-type') == 'application/json', (
|
||||||
|
'Expected content-type to be application/json, got %r' %
|
||||||
|
r.headers.get('content-type'))
|
||||||
|
raw_resp = r.content.decode('utf-8')
|
||||||
|
if route_style == self._ROUTE_STYLE_DOWNLOAD:
|
||||||
|
return RouteResult(raw_resp, r)
|
||||||
|
else:
|
||||||
|
return RouteResult(raw_resp)
|
||||||
|
elif r.status_code in (403, 404, 409):
|
||||||
|
raw_resp = r.content.decode('utf-8')
|
||||||
|
return RouteErrorResult(request_id, raw_resp)
|
||||||
|
else:
|
||||||
|
raise HttpError(request_id, r.status_code, r.text)
|
||||||
|
|
||||||
|
def _get_route_url(self, hostname, route_name):
|
||||||
|
"""Returns the URL of the route.
|
||||||
|
|
||||||
|
:param str hostname: Hostname to make the request to.
|
||||||
|
:param str route_name: Name of the route.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return 'https://{hostname}/{version}/{route_name}'.format(
|
||||||
|
hostname=hostname,
|
||||||
|
version=Dropbox._API_VERSION,
|
||||||
|
route_name=route_name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def _save_body_to_file(self, download_path, http_resp, chunksize=2**16):
|
||||||
|
"""
|
||||||
|
Saves the body of an HTTP response to a file.
|
||||||
|
|
||||||
|
:param str download_path: Local path to save data to.
|
||||||
|
:param http_resp: The HTTP response whose body will be saved.
|
||||||
|
:type http_resp: :class:`requests.models.Response`
|
||||||
|
:rtype: None
|
||||||
|
"""
|
||||||
|
with open(download_path, 'wb') as f:
|
||||||
|
with contextlib.closing(http_resp):
|
||||||
|
for c in http_resp.iter_content(chunksize):
|
||||||
|
f.write(c)
|
||||||
|
|
||||||
|
def with_path_root(self, path_root):
|
||||||
|
"""
|
||||||
|
Creates a clone of the Dropbox instance with the Dropbox-API-Path-Root header
|
||||||
|
as the appropriate serialized instance of PathRoot.
|
||||||
|
|
||||||
|
For more information, see
|
||||||
|
https://www.dropbox.com/developers/reference/namespace-guide#pathrootmodes
|
||||||
|
|
||||||
|
:param PathRoot path_root: instance of PathRoot to serialize into the headers field
|
||||||
|
:return: A :class: `Dropbox`
|
||||||
|
:rtype: Dropbox
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not isinstance(path_root, PathRoot):
|
||||||
|
raise ValueError("path_root must be an instance of PathRoot")
|
||||||
|
|
||||||
|
return self.clone(
|
||||||
|
headers={
|
||||||
|
PATH_ROOT_HEADER: stone_serializers.json_encode(PathRoot_validator, path_root)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
class Dropbox(_DropboxTransport, DropboxBase):
|
||||||
|
"""
|
||||||
|
Use this class to make requests to the Dropbox API using a user's access
|
||||||
|
token. Methods of this class are meant to act on the corresponding user's
|
||||||
|
Dropbox.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class DropboxTeam(_DropboxTransport, DropboxTeamBase):
|
||||||
|
"""
|
||||||
|
Use this class to make requests to the Dropbox API using a team's access
|
||||||
|
token. Methods of this class are meant to act on the team, but there is
|
||||||
|
also an :meth:`as_user` method for assuming a team member's identity.
|
||||||
|
"""
|
||||||
|
def as_admin(self, team_member_id):
|
||||||
|
"""
|
||||||
|
Allows a team credential to assume the identity of an administrator on the team
|
||||||
|
and perform operations on any team-owned content.
|
||||||
|
|
||||||
|
:param str team_member_id: team member id of administrator to perform actions with
|
||||||
|
:return: A :class:`Dropbox` object that can be used to query on behalf
|
||||||
|
of this admin of the team.
|
||||||
|
:rtype: Dropbox
|
||||||
|
"""
|
||||||
|
return self._get_dropbox_client_with_select_header('Dropbox-API-Select-Admin',
|
||||||
|
team_member_id)
|
||||||
|
|
||||||
|
def as_user(self, team_member_id):
|
||||||
|
"""
|
||||||
|
Allows a team credential to assume the identity of a member of the
|
||||||
|
team.
|
||||||
|
|
||||||
|
:param str team_member_id: team member id of team member to perform actions with
|
||||||
|
:return: A :class:`Dropbox` object that can be used to query on behalf
|
||||||
|
of this member of the team.
|
||||||
|
:rtype: Dropbox
|
||||||
|
"""
|
||||||
|
return self._get_dropbox_client_with_select_header('Dropbox-API-Select-User',
|
||||||
|
team_member_id)
|
||||||
|
|
||||||
|
def _get_dropbox_client_with_select_header(self, select_header_name, team_member_id):
|
||||||
|
"""
|
||||||
|
Get Dropbox client with modified headers
|
||||||
|
|
||||||
|
:param str select_header_name: Header name used to select users
|
||||||
|
:param str team_member_id: team member id of team member to perform actions with
|
||||||
|
:return: A :class:`Dropbox` object that can be used to query on behalf
|
||||||
|
of a member or admin of the team
|
||||||
|
:rtype: Dropbox
|
||||||
|
"""
|
||||||
|
|
||||||
|
new_headers = self._headers.copy() if self._headers else {}
|
||||||
|
new_headers[select_header_name] = team_member_id
|
||||||
|
return Dropbox(
|
||||||
|
self._oauth2_access_token,
|
||||||
|
max_retries_on_error=self._max_retries_on_error,
|
||||||
|
max_retries_on_rate_limit=self._max_retries_on_rate_limit,
|
||||||
|
timeout=self._timeout,
|
||||||
|
user_agent=self._raw_user_agent,
|
||||||
|
session=self._session,
|
||||||
|
headers=new_headers,
|
||||||
|
)
|
100
resources/lib/dropbox/exceptions.py
Normal file
100
resources/lib/dropbox/exceptions.py
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
class DropboxException(Exception):
|
||||||
|
"""All errors related to making an API request extend this."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, *args, **kwargs):
|
||||||
|
# A request_id can be shared with Dropbox Support to pinpoint the exact
|
||||||
|
# request that returns an error.
|
||||||
|
super(DropboxException, self).__init__(request_id, *args, **kwargs)
|
||||||
|
self.request_id = request_id
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return repr(self)
|
||||||
|
|
||||||
|
|
||||||
|
class ApiError(DropboxException):
|
||||||
|
"""Errors produced by the Dropbox API."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, error, user_message_text, user_message_locale):
|
||||||
|
"""
|
||||||
|
:param (str) request_id: A request_id can be shared with Dropbox
|
||||||
|
Support to pinpoint the exact request that returns an error.
|
||||||
|
:param error: An instance of the error data type for the route.
|
||||||
|
:param (str) user_message_text: A human-readable message that can be
|
||||||
|
displayed to the end user. Is None, if unavailable.
|
||||||
|
:param (str) user_message_locale: The locale of ``user_message_text``,
|
||||||
|
if present.
|
||||||
|
"""
|
||||||
|
super(ApiError, self).__init__(request_id, error)
|
||||||
|
self.error = error
|
||||||
|
self.user_message_text = user_message_text
|
||||||
|
self.user_message_locale = user_message_locale
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'ApiError({!r}, {})'.format(self.request_id, self.error)
|
||||||
|
|
||||||
|
|
||||||
|
class HttpError(DropboxException):
|
||||||
|
"""Errors produced at the HTTP layer."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, status_code, body):
|
||||||
|
super(HttpError, self).__init__(request_id, status_code, body)
|
||||||
|
self.status_code = status_code
|
||||||
|
self.body = body
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'HttpError({!r}, {}, {!r})'.format(self.request_id,
|
||||||
|
self.status_code, self.body)
|
||||||
|
|
||||||
|
|
||||||
|
class PathRootError(HttpError):
|
||||||
|
"""Error caused by an invalid path root."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, error=None):
|
||||||
|
super(PathRootError, self).__init__(request_id, 422, None)
|
||||||
|
self.error = error
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'PathRootError({!r}, {!r})'.format(self.request_id, self.error)
|
||||||
|
|
||||||
|
|
||||||
|
class BadInputError(HttpError):
|
||||||
|
"""Errors due to bad input parameters to an API Operation."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, message):
|
||||||
|
super(BadInputError, self).__init__(request_id, 400, message)
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'BadInputError({!r}, {!r})'.format(self.request_id, self.message)
|
||||||
|
|
||||||
|
|
||||||
|
class AuthError(HttpError):
|
||||||
|
"""Errors due to invalid authentication credentials."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, error):
|
||||||
|
super(AuthError, self).__init__(request_id, 401, None)
|
||||||
|
self.error = error
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'AuthError({!r}, {!r})'.format(self.request_id, self.error)
|
||||||
|
|
||||||
|
|
||||||
|
class RateLimitError(HttpError):
|
||||||
|
"""Error caused by rate limiting."""
|
||||||
|
|
||||||
|
def __init__(self, request_id, error=None, backoff=None):
|
||||||
|
super(RateLimitError, self).__init__(request_id, 429, None)
|
||||||
|
self.error = error
|
||||||
|
self.backoff = backoff
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'RateLimitError({!r}, {!r}, {!r})'.format(
|
||||||
|
self.request_id, self.error, self.backoff)
|
||||||
|
|
||||||
|
|
||||||
|
class InternalServerError(HttpError):
|
||||||
|
"""Errors due to a problem on Dropbox."""
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'InternalServerError({!r}, {}, {!r})'.format(
|
||||||
|
self.request_id, self.status_code, self.body)
|
3433
resources/lib/dropbox/file_properties.py
Normal file
3433
resources/lib/dropbox/file_properties.py
Normal file
File diff suppressed because it is too large
Load Diff
2172
resources/lib/dropbox/file_requests.py
Normal file
2172
resources/lib/dropbox/file_requests.py
Normal file
File diff suppressed because it is too large
Load Diff
14449
resources/lib/dropbox/files.py
Normal file
14449
resources/lib/dropbox/files.py
Normal file
File diff suppressed because it is too large
Load Diff
515
resources/lib/dropbox/oauth.py
Normal file
515
resources/lib/dropbox/oauth.py
Normal file
@ -0,0 +1,515 @@
|
|||||||
|
__all__ = [
|
||||||
|
'BadRequestException',
|
||||||
|
'BadStateException',
|
||||||
|
'CsrfException',
|
||||||
|
'DropboxOAuth2Flow',
|
||||||
|
'DropboxOAuth2FlowNoRedirect',
|
||||||
|
'NotApprovedException',
|
||||||
|
'OAuth2FlowNoRedirectResult',
|
||||||
|
'OAuth2FlowResult',
|
||||||
|
'ProviderException',
|
||||||
|
]
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import six
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
from .session import (
|
||||||
|
API_HOST,
|
||||||
|
WEB_HOST,
|
||||||
|
pinned_session,
|
||||||
|
)
|
||||||
|
|
||||||
|
if six.PY3:
|
||||||
|
url_path_quote = urllib.parse.quote # pylint: disable=no-member,useless-suppression
|
||||||
|
url_encode = urllib.parse.urlencode # pylint: disable=no-member,useless-suppression
|
||||||
|
else:
|
||||||
|
url_path_quote = urllib.quote # pylint: disable=no-member,useless-suppression
|
||||||
|
url_encode = urllib.urlencode # pylint: disable=no-member,useless-suppression
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth2FlowNoRedirectResult(object):
|
||||||
|
"""
|
||||||
|
Authorization information for an OAuth2Flow performed with no redirect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, access_token, account_id, user_id):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
access_token (str): Token to be used to authenticate later
|
||||||
|
requests.
|
||||||
|
account_id (str): The Dropbox user's account ID.
|
||||||
|
user_id (str): Deprecated (use account_id instead).
|
||||||
|
"""
|
||||||
|
self.access_token = access_token
|
||||||
|
self.account_id = account_id
|
||||||
|
self.user_id = user_id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'OAuth2FlowNoRedirectResult(%r, %r, %r)' % (
|
||||||
|
self.access_token,
|
||||||
|
self.account_id,
|
||||||
|
self.user_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class OAuth2FlowResult(OAuth2FlowNoRedirectResult):
|
||||||
|
"""
|
||||||
|
Authorization information for an OAuth2Flow with redirect.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, access_token, account_id, user_id, url_state):
|
||||||
|
"""
|
||||||
|
Same as OAuth2FlowNoRedirectResult but with url_state.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
url_state (str): The url state that was set by
|
||||||
|
:meth:`DropboxOAuth2Flow.start`.
|
||||||
|
"""
|
||||||
|
super(OAuth2FlowResult, self).__init__(
|
||||||
|
access_token, account_id, user_id)
|
||||||
|
self.url_state = url_state
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_no_redirect_result(cls, result, url_state):
|
||||||
|
assert isinstance(result, OAuth2FlowNoRedirectResult)
|
||||||
|
return cls(
|
||||||
|
result.access_token, result.account_id, result.user_id, url_state)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'OAuth2FlowResult(%r, %r, %r, %r)' % (
|
||||||
|
self.access_token,
|
||||||
|
self.account_id,
|
||||||
|
self.user_id,
|
||||||
|
self.url_state,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class DropboxOAuth2FlowBase(object):
|
||||||
|
|
||||||
|
def __init__(self, consumer_key, consumer_secret, locale=None):
|
||||||
|
self.consumer_key = consumer_key
|
||||||
|
self.consumer_secret = consumer_secret
|
||||||
|
self.locale = locale
|
||||||
|
self.requests_session = pinned_session()
|
||||||
|
|
||||||
|
def _get_authorize_url(self, redirect_uri, state):
|
||||||
|
params = dict(response_type='code',
|
||||||
|
client_id=self.consumer_key)
|
||||||
|
if redirect_uri is not None:
|
||||||
|
params['redirect_uri'] = redirect_uri
|
||||||
|
if state is not None:
|
||||||
|
params['state'] = state
|
||||||
|
|
||||||
|
return self.build_url('/oauth2/authorize', params, WEB_HOST)
|
||||||
|
|
||||||
|
def _finish(self, code, redirect_uri):
|
||||||
|
url = self.build_url('/oauth2/token')
|
||||||
|
params = {'grant_type': 'authorization_code',
|
||||||
|
'code': code,
|
||||||
|
'client_id': self.consumer_key,
|
||||||
|
'client_secret': self.consumer_secret,
|
||||||
|
}
|
||||||
|
if self.locale is not None:
|
||||||
|
params['locale'] = self.locale
|
||||||
|
if redirect_uri is not None:
|
||||||
|
params['redirect_uri'] = redirect_uri
|
||||||
|
|
||||||
|
resp = self.requests_session.post(url, data=params)
|
||||||
|
resp.raise_for_status()
|
||||||
|
|
||||||
|
d = resp.json()
|
||||||
|
|
||||||
|
if 'team_id' in d:
|
||||||
|
account_id = d['team_id']
|
||||||
|
else:
|
||||||
|
account_id = d['account_id']
|
||||||
|
|
||||||
|
access_token = d['access_token']
|
||||||
|
uid = d['uid']
|
||||||
|
|
||||||
|
return OAuth2FlowNoRedirectResult(
|
||||||
|
access_token,
|
||||||
|
account_id,
|
||||||
|
uid)
|
||||||
|
|
||||||
|
def build_path(self, target, params=None):
|
||||||
|
"""Build the path component for an API URL.
|
||||||
|
|
||||||
|
This method urlencodes the parameters, adds them
|
||||||
|
to the end of the target url, and puts a marker for the API
|
||||||
|
version in front.
|
||||||
|
|
||||||
|
:param str target: A target url (e.g. '/files') to build upon.
|
||||||
|
:param dict params: Optional dictionary of parameters (name to value).
|
||||||
|
:return: The path and parameters components of an API URL.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if six.PY2 and isinstance(target, six.text_type):
|
||||||
|
target = target.encode('utf8')
|
||||||
|
|
||||||
|
target_path = url_path_quote(target)
|
||||||
|
|
||||||
|
params = params or {}
|
||||||
|
params = params.copy()
|
||||||
|
|
||||||
|
if self.locale:
|
||||||
|
params['locale'] = self.locale
|
||||||
|
|
||||||
|
if params:
|
||||||
|
query_string = _params_to_urlencoded(params)
|
||||||
|
return "%s?%s" % (target_path, query_string)
|
||||||
|
else:
|
||||||
|
return target_path
|
||||||
|
|
||||||
|
def build_url(self, target, params=None, host=API_HOST):
|
||||||
|
"""Build an API URL.
|
||||||
|
|
||||||
|
This method adds scheme and hostname to the path
|
||||||
|
returned from build_path.
|
||||||
|
|
||||||
|
:param str target: A target url (e.g. '/files') to build upon.
|
||||||
|
:param dict params: Optional dictionary of parameters (name to value).
|
||||||
|
:return: The full API URL.
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
return "https://%s%s" % (host, self.build_path(target, params))
|
||||||
|
|
||||||
|
|
||||||
|
class DropboxOAuth2FlowNoRedirect(DropboxOAuth2FlowBase):
|
||||||
|
"""
|
||||||
|
OAuth 2 authorization helper for apps that can't provide a redirect URI
|
||||||
|
(such as the command-line example apps).
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
from dropbox import DropboxOAuth2FlowNoRedirect
|
||||||
|
|
||||||
|
auth_flow = DropboxOAuth2FlowNoRedirect(APP_KEY, APP_SECRET)
|
||||||
|
|
||||||
|
authorize_url = auth_flow.start()
|
||||||
|
print "1. Go to: " + authorize_url
|
||||||
|
print "2. Click \\"Allow\\" (you might have to log in first)."
|
||||||
|
print "3. Copy the authorization code."
|
||||||
|
auth_code = raw_input("Enter the authorization code here: ").strip()
|
||||||
|
|
||||||
|
try:
|
||||||
|
oauth_result = auth_flow.finish(auth_code)
|
||||||
|
except Exception, e:
|
||||||
|
print('Error: %s' % (e,))
|
||||||
|
return
|
||||||
|
|
||||||
|
dbx = Dropbox(oauth_result.access_token)
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, consumer_key, consumer_secret, locale=None): # noqa: E501; pylint: disable=useless-super-delegation
|
||||||
|
"""
|
||||||
|
Construct an instance.
|
||||||
|
|
||||||
|
Parameters
|
||||||
|
:param str consumer_key: Your API app's "app key".
|
||||||
|
:param str consumer_secret: Your API app's "app secret".
|
||||||
|
:param str locale: The locale of the user of your application. For
|
||||||
|
example "en" or "en_US". Some API calls return localized data and
|
||||||
|
error messages; this setting tells the server which locale to use.
|
||||||
|
By default, the server uses "en_US".
|
||||||
|
"""
|
||||||
|
# pylint: disable=useless-super-delegation
|
||||||
|
super(DropboxOAuth2FlowNoRedirect, self).__init__(
|
||||||
|
consumer_key,
|
||||||
|
consumer_secret,
|
||||||
|
locale,
|
||||||
|
)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
"""
|
||||||
|
Starts the OAuth 2 authorization process.
|
||||||
|
|
||||||
|
:return: The URL for a page on Dropbox's website. This page will let
|
||||||
|
the user "approve" your app, which gives your app permission to
|
||||||
|
access the user's Dropbox account. Tell the user to visit this URL
|
||||||
|
and approve your app.
|
||||||
|
"""
|
||||||
|
return self._get_authorize_url(None, None)
|
||||||
|
|
||||||
|
def finish(self, code):
|
||||||
|
"""
|
||||||
|
If the user approves your app, they will be presented with an
|
||||||
|
"authorization code". Have the user copy/paste that authorization code
|
||||||
|
into your app and then call this method to get an access token.
|
||||||
|
|
||||||
|
:param str code: The authorization code shown to the user when they
|
||||||
|
approved your app.
|
||||||
|
:rtype: OAuth2FlowNoRedirectResult
|
||||||
|
:raises: The same exceptions as :meth:`DropboxOAuth2Flow.finish()`.
|
||||||
|
"""
|
||||||
|
return self._finish(code, None)
|
||||||
|
|
||||||
|
|
||||||
|
class DropboxOAuth2Flow(DropboxOAuth2FlowBase):
|
||||||
|
"""
|
||||||
|
OAuth 2 authorization helper. Use this for web apps.
|
||||||
|
|
||||||
|
OAuth 2 has a two-step authorization process. The first step is having the
|
||||||
|
user authorize your app. The second involves getting an OAuth 2 access
|
||||||
|
token from Dropbox.
|
||||||
|
|
||||||
|
Example::
|
||||||
|
|
||||||
|
from dropbox import DropboxOAuth2Flow
|
||||||
|
|
||||||
|
def get_dropbox_auth_flow(web_app_session):
|
||||||
|
redirect_uri = "https://my-web-server.org/dropbox-auth-finish"
|
||||||
|
return DropboxOAuth2Flow(
|
||||||
|
APP_KEY, APP_SECRET, redirect_uri, web_app_session,
|
||||||
|
"dropbox-auth-csrf-token")
|
||||||
|
|
||||||
|
# URL handler for /dropbox-auth-start
|
||||||
|
def dropbox_auth_start(web_app_session, request):
|
||||||
|
authorize_url = get_dropbox_auth_flow(web_app_session).start()
|
||||||
|
redirect_to(authorize_url)
|
||||||
|
|
||||||
|
# URL handler for /dropbox-auth-finish
|
||||||
|
def dropbox_auth_finish(web_app_session, request):
|
||||||
|
try:
|
||||||
|
oauth_result = \\
|
||||||
|
get_dropbox_auth_flow(web_app_session).finish(
|
||||||
|
request.query_params)
|
||||||
|
except BadRequestException, e:
|
||||||
|
http_status(400)
|
||||||
|
except BadStateException, e:
|
||||||
|
# Start the auth flow again.
|
||||||
|
redirect_to("/dropbox-auth-start")
|
||||||
|
except CsrfException, e:
|
||||||
|
http_status(403)
|
||||||
|
except NotApprovedException, e:
|
||||||
|
flash('Not approved? Why not?')
|
||||||
|
return redirect_to("/home")
|
||||||
|
except ProviderException, e:
|
||||||
|
logger.log("Auth error: %s" % (e,))
|
||||||
|
http_status(403)
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, consumer_key, consumer_secret, redirect_uri, session,
|
||||||
|
csrf_token_session_key, locale=None):
|
||||||
|
"""
|
||||||
|
Construct an instance.
|
||||||
|
|
||||||
|
:param str consumer_key: Your API app's "app key".
|
||||||
|
:param str consumer_secret: Your API app's "app secret".
|
||||||
|
:param str redirect_uri: The URI that the Dropbox server will redirect
|
||||||
|
the user to after the user finishes authorizing your app. This URI
|
||||||
|
must be HTTPS-based and pre-registered with the Dropbox servers,
|
||||||
|
though localhost URIs are allowed without pre-registration and can
|
||||||
|
be either HTTP or HTTPS.
|
||||||
|
:param dict session: A dict-like object that represents the current
|
||||||
|
user's web session (will be used to save the CSRF token).
|
||||||
|
:param str csrf_token_session_key: The key to use when storing the CSRF
|
||||||
|
token in the session (for example: "dropbox-auth-csrf-token").
|
||||||
|
:param str locale: The locale of the user of your application. For
|
||||||
|
example "en" or "en_US". Some API calls return localized data and
|
||||||
|
error messages; this setting tells the server which locale to use.
|
||||||
|
By default, the server uses "en_US".
|
||||||
|
"""
|
||||||
|
super(DropboxOAuth2Flow, self).__init__(consumer_key, consumer_secret, locale)
|
||||||
|
self.redirect_uri = redirect_uri
|
||||||
|
self.session = session
|
||||||
|
self.csrf_token_session_key = csrf_token_session_key
|
||||||
|
|
||||||
|
def start(self, url_state=None):
|
||||||
|
"""
|
||||||
|
Starts the OAuth 2 authorization process.
|
||||||
|
|
||||||
|
This function builds an "authorization URL". You should redirect your
|
||||||
|
user's browser to this URL, which will give them an opportunity to
|
||||||
|
grant your app access to their Dropbox account. When the user
|
||||||
|
completes this process, they will be automatically redirected to the
|
||||||
|
``redirect_uri`` you passed in to the constructor.
|
||||||
|
|
||||||
|
This function will also save a CSRF token to
|
||||||
|
``session[csrf_token_session_key]`` (as provided to the constructor).
|
||||||
|
This CSRF token will be checked on :meth:`finish()` to prevent request
|
||||||
|
forgery.
|
||||||
|
|
||||||
|
:param str url_state: Any data that you would like to keep in the URL
|
||||||
|
through the authorization process. This exact value will be
|
||||||
|
returned to you by :meth:`finish()`.
|
||||||
|
:return: The URL for a page on Dropbox's website. This page will let
|
||||||
|
the user "approve" your app, which gives your app permission to
|
||||||
|
access the user's Dropbox account. Tell the user to visit this URL
|
||||||
|
and approve your app.
|
||||||
|
"""
|
||||||
|
csrf_token = base64.urlsafe_b64encode(os.urandom(16)).decode('ascii')
|
||||||
|
state = csrf_token
|
||||||
|
if url_state is not None:
|
||||||
|
state += "|" + url_state
|
||||||
|
self.session[self.csrf_token_session_key] = csrf_token
|
||||||
|
|
||||||
|
return self._get_authorize_url(self.redirect_uri, state)
|
||||||
|
|
||||||
|
def finish(self, query_params):
|
||||||
|
"""
|
||||||
|
Call this after the user has visited the authorize URL (see
|
||||||
|
:meth:`start()`), approved your app and was redirected to your redirect
|
||||||
|
URI.
|
||||||
|
|
||||||
|
:param dict query_params: The query parameters on the GET request to
|
||||||
|
your redirect URI.
|
||||||
|
:rtype: OAuth2FlowResult
|
||||||
|
:raises: :class:`BadRequestException` If the redirect URL was missing
|
||||||
|
parameters or if the given parameters were not valid.
|
||||||
|
:raises: :class:`BadStateException` If there's no CSRF token in the
|
||||||
|
session.
|
||||||
|
:raises: :class:`CsrfException` If the ``state`` query parameter
|
||||||
|
doesn't contain the CSRF token from the user's session.
|
||||||
|
:raises: :class:`NotApprovedException` If the user chose not to
|
||||||
|
approve your app.
|
||||||
|
:raises: :class:`ProviderException` If Dropbox redirected to your
|
||||||
|
redirect URI with some unexpected error identifier and error message.
|
||||||
|
"""
|
||||||
|
# Check well-formedness of request.
|
||||||
|
|
||||||
|
state = query_params.get('state')
|
||||||
|
if state is None:
|
||||||
|
raise BadRequestException("Missing query parameter 'state'.")
|
||||||
|
|
||||||
|
error = query_params.get('error')
|
||||||
|
error_description = query_params.get('error_description')
|
||||||
|
code = query_params.get('code')
|
||||||
|
|
||||||
|
if error is not None and code is not None:
|
||||||
|
raise BadRequestException(
|
||||||
|
"Query parameters 'code' and 'error' are both set; "
|
||||||
|
"only one must be set.")
|
||||||
|
if error is None and code is None:
|
||||||
|
raise BadRequestException(
|
||||||
|
"Neither query parameter 'code' or 'error' is set.")
|
||||||
|
|
||||||
|
# Check CSRF token
|
||||||
|
|
||||||
|
if self.csrf_token_session_key not in self.session:
|
||||||
|
raise BadStateException('Missing CSRF token in session.')
|
||||||
|
csrf_token_from_session = self.session[self.csrf_token_session_key]
|
||||||
|
if len(csrf_token_from_session) <= 20:
|
||||||
|
raise AssertionError('CSRF token unexpectedly short: %r' %
|
||||||
|
csrf_token_from_session)
|
||||||
|
|
||||||
|
split_pos = state.find('|')
|
||||||
|
if split_pos < 0:
|
||||||
|
given_csrf_token = state
|
||||||
|
url_state = None
|
||||||
|
else:
|
||||||
|
given_csrf_token = state[0:split_pos]
|
||||||
|
url_state = state[split_pos + 1:]
|
||||||
|
|
||||||
|
if not _safe_equals(csrf_token_from_session, given_csrf_token):
|
||||||
|
raise CsrfException('expected %r, got %r' %
|
||||||
|
(csrf_token_from_session, given_csrf_token))
|
||||||
|
|
||||||
|
del self.session[self.csrf_token_session_key]
|
||||||
|
|
||||||
|
# Check for error identifier
|
||||||
|
|
||||||
|
if error is not None:
|
||||||
|
if error == 'access_denied':
|
||||||
|
# The user clicked "Deny"
|
||||||
|
if error_description is None:
|
||||||
|
raise NotApprovedException(
|
||||||
|
'No additional description from Dropbox')
|
||||||
|
else:
|
||||||
|
raise NotApprovedException(
|
||||||
|
'Additional description from Dropbox: %s' %
|
||||||
|
error_description)
|
||||||
|
else:
|
||||||
|
# All other errors
|
||||||
|
full_message = error
|
||||||
|
if error_description is not None:
|
||||||
|
full_message += ": " + error_description
|
||||||
|
raise ProviderException(full_message)
|
||||||
|
|
||||||
|
# If everything went ok, make the network call to get an access token.
|
||||||
|
|
||||||
|
no_redirect_result = self._finish(code, self.redirect_uri)
|
||||||
|
return OAuth2FlowResult.from_no_redirect_result(
|
||||||
|
no_redirect_result, url_state)
|
||||||
|
|
||||||
|
|
||||||
|
class BadRequestException(Exception):
|
||||||
|
"""
|
||||||
|
Thrown if the redirect URL was missing parameters or if the
|
||||||
|
given parameters were not valid.
|
||||||
|
|
||||||
|
The recommended action is to show an HTTP 400 error page.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BadStateException(Exception):
|
||||||
|
"""
|
||||||
|
Thrown if all the parameters are correct, but there's no CSRF token in the
|
||||||
|
session. This probably means that the session expired.
|
||||||
|
|
||||||
|
The recommended action is to redirect the user's browser to try the
|
||||||
|
approval process again.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class CsrfException(Exception):
|
||||||
|
"""
|
||||||
|
Thrown if the given 'state' parameter doesn't contain the CSRF token from
|
||||||
|
the user's session. This is blocked to prevent CSRF attacks.
|
||||||
|
|
||||||
|
The recommended action is to respond with an HTTP 403 error page.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class NotApprovedException(Exception):
|
||||||
|
"""
|
||||||
|
The user chose not to approve your app.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ProviderException(Exception):
|
||||||
|
"""
|
||||||
|
Dropbox redirected to your redirect URI with some unexpected error
|
||||||
|
identifier and error message.
|
||||||
|
|
||||||
|
The recommended action is to log the error, tell the user something went
|
||||||
|
wrong, and let them try again.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def _safe_equals(a, b):
|
||||||
|
if len(a) != len(b):
|
||||||
|
return False
|
||||||
|
res = 0
|
||||||
|
for ca, cb in zip(a, b):
|
||||||
|
res |= ord(ca) ^ ord(cb)
|
||||||
|
return res == 0
|
||||||
|
|
||||||
|
|
||||||
|
def _params_to_urlencoded(params):
|
||||||
|
"""
|
||||||
|
Returns a application/x-www-form-urlencoded ``str`` representing the
|
||||||
|
key/value pairs in ``params``.
|
||||||
|
|
||||||
|
Keys are values are ``str()``'d before calling ``urllib.urlencode``, with
|
||||||
|
the exception of unicode objects which are utf8-encoded.
|
||||||
|
"""
|
||||||
|
def encode(o):
|
||||||
|
if isinstance(o, six.binary_type):
|
||||||
|
return o
|
||||||
|
else:
|
||||||
|
if isinstance(o, six.text_type):
|
||||||
|
return o.encode('utf-8')
|
||||||
|
else:
|
||||||
|
return str(o).encode('utf-8')
|
||||||
|
|
||||||
|
utf8_params = {encode(k): encode(v) for k, v in six.iteritems(params)}
|
||||||
|
return url_encode(utf8_params)
|
4575
resources/lib/dropbox/paper.py
Normal file
4575
resources/lib/dropbox/paper.py
Normal file
File diff suppressed because it is too large
Load Diff
2
resources/lib/dropbox/pkg_resources.py
Normal file
2
resources/lib/dropbox/pkg_resources.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
def resource_filename(package_or_requirement, resource_name):
|
||||||
|
return resource_name
|
51
resources/lib/dropbox/session.py
Normal file
51
resources/lib/dropbox/session.py
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import pkg_resources
|
||||||
|
import os
|
||||||
|
import ssl
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from requests.adapters import HTTPAdapter
|
||||||
|
from urllib3.poolmanager import PoolManager
|
||||||
|
|
||||||
|
API_DOMAIN = os.environ.get('DROPBOX_API_DOMAIN',
|
||||||
|
os.environ.get('DROPBOX_DOMAIN', '.dropboxapi.com'))
|
||||||
|
|
||||||
|
WEB_DOMAIN = os.environ.get('DROPBOX_WEB_DOMAIN',
|
||||||
|
os.environ.get('DROPBOX_DOMAIN', '.dropbox.com'))
|
||||||
|
|
||||||
|
# Default short hostname for RPC-style routes.
|
||||||
|
HOST_API = 'api'
|
||||||
|
|
||||||
|
# Default short hostname for upload and download-style routes.
|
||||||
|
HOST_CONTENT = 'content'
|
||||||
|
|
||||||
|
# Default short hostname for longpoll routes.
|
||||||
|
HOST_NOTIFY = 'notify'
|
||||||
|
|
||||||
|
# Default short hostname for the Drobox website.
|
||||||
|
HOST_WWW = 'www'
|
||||||
|
|
||||||
|
API_HOST = os.environ.get('DROPBOX_API_HOST', HOST_API + API_DOMAIN)
|
||||||
|
API_CONTENT_HOST = os.environ.get('DROPBOX_API_CONTENT_HOST', HOST_CONTENT + API_DOMAIN)
|
||||||
|
API_NOTIFICATION_HOST = os.environ.get('DROPBOX_API_NOTIFY_HOST', HOST_NOTIFY + API_DOMAIN)
|
||||||
|
WEB_HOST = os.environ.get('DROPBOX_WEB_HOST', HOST_WWW + WEB_DOMAIN)
|
||||||
|
|
||||||
|
_TRUSTED_CERT_FILE = pkg_resources.resource_filename(__name__, 'trusted-certs.crt')
|
||||||
|
|
||||||
|
# TODO(kelkabany): We probably only want to instantiate this once so that even
|
||||||
|
# if multiple Dropbox objects are instantiated, they all share the same pool.
|
||||||
|
class _SSLAdapter(HTTPAdapter):
|
||||||
|
def init_poolmanager(self, connections, maxsize, block=False, **_):
|
||||||
|
self.poolmanager = PoolManager(
|
||||||
|
num_pools=connections,
|
||||||
|
maxsize=maxsize,
|
||||||
|
block=block,
|
||||||
|
cert_reqs=ssl.CERT_REQUIRED,
|
||||||
|
ca_certs=_TRUSTED_CERT_FILE,
|
||||||
|
)
|
||||||
|
|
||||||
|
def pinned_session(pool_maxsize=8):
|
||||||
|
http_adapter = _SSLAdapter(pool_connections=4, pool_maxsize=pool_maxsize)
|
||||||
|
_session = requests.session()
|
||||||
|
_session.mount('https://', http_adapter)
|
||||||
|
|
||||||
|
return _session
|
19911
resources/lib/dropbox/sharing.py
Normal file
19911
resources/lib/dropbox/sharing.py
Normal file
File diff suppressed because it is too large
Load Diff
152
resources/lib/dropbox/stone_base.py
Normal file
152
resources/lib/dropbox/stone_base.py
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
"""
|
||||||
|
Helpers for representing Stone data types in Python.
|
||||||
|
|
||||||
|
This module should be dropped into a project that requires the use of Stone. In
|
||||||
|
the future, this could be imported from a pre-installed Python package, rather
|
||||||
|
than being added to a project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
import functools
|
||||||
|
|
||||||
|
try:
|
||||||
|
from . import stone_validators as bv
|
||||||
|
except (ImportError, SystemError, ValueError):
|
||||||
|
# Catch errors raised when importing a relative module when not in a package.
|
||||||
|
# This makes testing this file directly (outside of a package) easier.
|
||||||
|
import stone_validators as bv # type: ignore
|
||||||
|
|
||||||
|
_MYPY = False
|
||||||
|
if _MYPY:
|
||||||
|
import typing # noqa: F401 # pylint: disable=import-error,unused-import,useless-suppression
|
||||||
|
|
||||||
|
class AnnotationType(object):
|
||||||
|
# This is a base class for all annotation types.
|
||||||
|
pass
|
||||||
|
|
||||||
|
if _MYPY:
|
||||||
|
T = typing.TypeVar('T', bound=AnnotationType)
|
||||||
|
U = typing.TypeVar('U')
|
||||||
|
|
||||||
|
class Struct(object):
|
||||||
|
# This is a base class for all classes representing Stone structs.
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
# type: (typing.Type[T], typing.Text, typing.Callable[[T, U], U]) -> None
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Union(object):
|
||||||
|
# TODO(kelkabany): Possible optimization is to remove _value if a
|
||||||
|
# union is composed of only symbols.
|
||||||
|
__slots__ = ['_tag', '_value']
|
||||||
|
_tagmap = {} # type: typing.Dict[typing.Text, bv.Validator]
|
||||||
|
_permissioned_tagmaps = set() # type: typing.Set[typing.Text]
|
||||||
|
|
||||||
|
def __init__(self, tag, value=None):
|
||||||
|
validator = None
|
||||||
|
tagmap_names = ['_{}_tagmap'.format(map_name) for map_name in self._permissioned_tagmaps]
|
||||||
|
for tagmap_name in ['_tagmap'] + tagmap_names:
|
||||||
|
if tag in getattr(self, tagmap_name):
|
||||||
|
validator = getattr(self, tagmap_name)[tag]
|
||||||
|
assert validator is not None, 'Invalid tag %r.' % tag
|
||||||
|
if isinstance(validator, bv.Void):
|
||||||
|
assert value is None, 'Void type union member must have None value.'
|
||||||
|
elif isinstance(validator, (bv.Struct, bv.Union)):
|
||||||
|
validator.validate_type_only(value)
|
||||||
|
else:
|
||||||
|
validator.validate(value)
|
||||||
|
self._tag = tag
|
||||||
|
self._value = value
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
# Also need to check if one class is a subclass of another. If one union extends another,
|
||||||
|
# the common fields should be able to be compared to each other.
|
||||||
|
return (
|
||||||
|
isinstance(other, Union) and
|
||||||
|
(isinstance(self, other.__class__) or isinstance(other, self.__class__)) and
|
||||||
|
self._tag == other._tag and self._value == other._value
|
||||||
|
)
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
return not self == other
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash((self._tag, self._value))
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
# type: (typing.Type[T], typing.Text, typing.Callable[[T, U], U]) -> None
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _is_tag_present(cls, tag, caller_permissions):
|
||||||
|
assert tag, 'tag value should not be None'
|
||||||
|
|
||||||
|
if tag in cls._tagmap:
|
||||||
|
return True
|
||||||
|
|
||||||
|
for extra_permission in caller_permissions.permissions:
|
||||||
|
tagmap_name = '_{}_tagmap'.format(extra_permission)
|
||||||
|
if hasattr(cls, tagmap_name) and tag in getattr(cls, tagmap_name):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _get_val_data_type(cls, tag, caller_permissions):
|
||||||
|
assert tag, 'tag value should not be None'
|
||||||
|
|
||||||
|
for extra_permission in caller_permissions.permissions:
|
||||||
|
tagmap_name = '_{}_tagmap'.format(extra_permission)
|
||||||
|
if hasattr(cls, tagmap_name) and tag in getattr(cls, tagmap_name):
|
||||||
|
return getattr(cls, tagmap_name)[tag]
|
||||||
|
|
||||||
|
return cls._tagmap[tag]
|
||||||
|
|
||||||
|
class Route(object):
|
||||||
|
|
||||||
|
def __init__(self, name, version, deprecated, arg_type, result_type, error_type, attrs):
|
||||||
|
self.name = name
|
||||||
|
self.version = version
|
||||||
|
self.deprecated = deprecated
|
||||||
|
self.arg_type = arg_type
|
||||||
|
self.result_type = result_type
|
||||||
|
self.error_type = error_type
|
||||||
|
assert isinstance(attrs, dict), 'Expected dict, got %r' % attrs
|
||||||
|
self.attrs = attrs
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Route({!r}, {!r}, {!r}, {!r}, {!r}, {!r}, {!r})'.format(
|
||||||
|
self.name,
|
||||||
|
self.version,
|
||||||
|
self.deprecated,
|
||||||
|
self.arg_type,
|
||||||
|
self.result_type,
|
||||||
|
self.error_type,
|
||||||
|
self.attrs)
|
||||||
|
|
||||||
|
# helper functions used when constructing custom annotation processors
|
||||||
|
|
||||||
|
# put this here so that every other file doesn't need to import functools
|
||||||
|
partially_apply = functools.partial
|
||||||
|
|
||||||
|
def make_struct_annotation_processor(annotation_type, processor):
|
||||||
|
def g(field_path, struct):
|
||||||
|
if struct is None:
|
||||||
|
return struct
|
||||||
|
struct._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
return struct
|
||||||
|
return g
|
||||||
|
|
||||||
|
def make_list_annotation_processor(processor):
|
||||||
|
def g(field_path, list_):
|
||||||
|
if list_ is None:
|
||||||
|
return list_
|
||||||
|
return [processor('{}[{}]'.format(field_path, idx), x) for idx, x in enumerate(list_)]
|
||||||
|
return g
|
||||||
|
|
||||||
|
def make_map_value_annotation_processor(processor):
|
||||||
|
def g(field_path, map_):
|
||||||
|
if map_ is None:
|
||||||
|
return map_
|
||||||
|
return {k: processor('{}[{}]'.format(field_path, repr(k)), v) for k, v in map_.items()}
|
||||||
|
return g
|
1081
resources/lib/dropbox/stone_serializers.py
Normal file
1081
resources/lib/dropbox/stone_serializers.py
Normal file
File diff suppressed because it is too large
Load Diff
673
resources/lib/dropbox/stone_validators.py
Normal file
673
resources/lib/dropbox/stone_validators.py
Normal file
@ -0,0 +1,673 @@
|
|||||||
|
"""
|
||||||
|
Defines classes to represent each Stone type in Python. These classes should
|
||||||
|
be used to validate Python objects and normalize them for a given type.
|
||||||
|
|
||||||
|
The data types defined here should not be specific to an RPC or serialization
|
||||||
|
format.
|
||||||
|
|
||||||
|
This module should be dropped into a project that requires the use of Stone. In
|
||||||
|
the future, this could be imported from a pre-installed Python package, rather
|
||||||
|
than being added to a project.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from abc import ABCMeta, abstractmethod
|
||||||
|
import datetime
|
||||||
|
import hashlib
|
||||||
|
import math
|
||||||
|
import numbers
|
||||||
|
import re
|
||||||
|
import six
|
||||||
|
|
||||||
|
_MYPY = False
|
||||||
|
if _MYPY:
|
||||||
|
import typing # noqa: F401 # pylint: disable=import-error,unused-import,useless-suppression
|
||||||
|
|
||||||
|
# See <http://python3porting.com/differences.html#buffer>
|
||||||
|
if six.PY3:
|
||||||
|
_binary_types = (bytes, memoryview) # noqa: E501,F821 # pylint: disable=undefined-variable,useless-suppression
|
||||||
|
else:
|
||||||
|
_binary_types = (bytes, buffer) # noqa: E501,F821 # pylint: disable=undefined-variable,useless-suppression
|
||||||
|
|
||||||
|
|
||||||
|
class ValidationError(Exception):
|
||||||
|
"""Raised when a value doesn't pass validation by its validator."""
|
||||||
|
|
||||||
|
def __init__(self, message, parent=None):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
message (str): Error message detailing validation failure.
|
||||||
|
parent (str): Adds the parent as the closest reference point for
|
||||||
|
the error. Use :meth:`add_parent` to add more.
|
||||||
|
"""
|
||||||
|
super(ValidationError, self).__init__(message)
|
||||||
|
self.message = message
|
||||||
|
self._parents = []
|
||||||
|
if parent:
|
||||||
|
self._parents.append(parent)
|
||||||
|
|
||||||
|
def add_parent(self, parent):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
parent (str): Adds the parent to the top of the tree of references
|
||||||
|
that lead to the validator that failed.
|
||||||
|
"""
|
||||||
|
self._parents.append(parent)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""
|
||||||
|
Returns:
|
||||||
|
str: A descriptive message of the validation error that may also
|
||||||
|
include the path to the validator that failed.
|
||||||
|
"""
|
||||||
|
if self._parents:
|
||||||
|
return '{}: {}'.format('.'.join(self._parents[::-1]), self.message)
|
||||||
|
else:
|
||||||
|
return self.message
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
# Not a perfect repr, but includes the error location information.
|
||||||
|
return 'ValidationError(%r)' % six.text_type(self)
|
||||||
|
|
||||||
|
|
||||||
|
def generic_type_name(v):
|
||||||
|
"""Return a descriptive type name that isn't Python specific. For example,
|
||||||
|
an int value will return 'integer' rather than 'int'."""
|
||||||
|
if isinstance(v, bool):
|
||||||
|
# Must come before any numbers checks since booleans are integers too
|
||||||
|
return 'boolean'
|
||||||
|
elif isinstance(v, numbers.Integral):
|
||||||
|
# Must come before real numbers check since integrals are reals too
|
||||||
|
return 'integer'
|
||||||
|
elif isinstance(v, numbers.Real):
|
||||||
|
return 'float'
|
||||||
|
elif isinstance(v, (tuple, list)):
|
||||||
|
return 'list'
|
||||||
|
elif isinstance(v, six.string_types):
|
||||||
|
return 'string'
|
||||||
|
elif v is None:
|
||||||
|
return 'null'
|
||||||
|
else:
|
||||||
|
return type(v).__name__
|
||||||
|
|
||||||
|
|
||||||
|
class Validator(object):
|
||||||
|
"""All primitive and composite data types should be a subclass of this."""
|
||||||
|
__metaclass__ = ABCMeta
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def validate(self, val):
|
||||||
|
"""Validates that val is of this data type.
|
||||||
|
|
||||||
|
Returns: A normalized value if validation succeeds.
|
||||||
|
Raises: ValidationError
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def has_default(self):
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
raise AssertionError('No default available.')
|
||||||
|
|
||||||
|
|
||||||
|
class Primitive(Validator):
|
||||||
|
"""A basic type that is defined by Stone."""
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Boolean(Primitive):
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, bool):
|
||||||
|
raise ValidationError('%r is not a valid boolean' % val)
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class Integer(Primitive):
|
||||||
|
"""
|
||||||
|
Do not use this class directly. Extend it and specify a 'minimum' and
|
||||||
|
'maximum' value as class variables for a more restrictive integer range.
|
||||||
|
"""
|
||||||
|
minimum = None # type: typing.Optional[int]
|
||||||
|
maximum = None # type: typing.Optional[int]
|
||||||
|
|
||||||
|
def __init__(self, min_value=None, max_value=None):
|
||||||
|
"""
|
||||||
|
A more restrictive minimum or maximum value can be specified than the
|
||||||
|
range inherent to the defined type.
|
||||||
|
"""
|
||||||
|
if min_value is not None:
|
||||||
|
assert isinstance(min_value, numbers.Integral), \
|
||||||
|
'min_value must be an integral number'
|
||||||
|
assert min_value >= self.minimum, \
|
||||||
|
'min_value cannot be less than the minimum value for this ' \
|
||||||
|
'type (%d < %d)' % (min_value, self.minimum)
|
||||||
|
self.minimum = min_value
|
||||||
|
if max_value is not None:
|
||||||
|
assert isinstance(max_value, numbers.Integral), \
|
||||||
|
'max_value must be an integral number'
|
||||||
|
assert max_value <= self.maximum, \
|
||||||
|
'max_value cannot be greater than the maximum value for ' \
|
||||||
|
'this type (%d < %d)' % (max_value, self.maximum)
|
||||||
|
self.maximum = max_value
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, numbers.Integral):
|
||||||
|
raise ValidationError('expected integer, got %s'
|
||||||
|
% generic_type_name(val))
|
||||||
|
elif not (self.minimum <= val <= self.maximum):
|
||||||
|
raise ValidationError('%d is not within range [%d, %d]'
|
||||||
|
% (val, self.minimum, self.maximum))
|
||||||
|
return val
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s()' % self.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
class Int32(Integer):
|
||||||
|
minimum = -2**31
|
||||||
|
maximum = 2**31 - 1
|
||||||
|
|
||||||
|
|
||||||
|
class UInt32(Integer):
|
||||||
|
minimum = 0
|
||||||
|
maximum = 2**32 - 1
|
||||||
|
|
||||||
|
|
||||||
|
class Int64(Integer):
|
||||||
|
minimum = -2**63
|
||||||
|
maximum = 2**63 - 1
|
||||||
|
|
||||||
|
|
||||||
|
class UInt64(Integer):
|
||||||
|
minimum = 0
|
||||||
|
maximum = 2**64 - 1
|
||||||
|
|
||||||
|
|
||||||
|
class Real(Primitive):
|
||||||
|
"""
|
||||||
|
Do not use this class directly. Extend it and optionally set a 'minimum'
|
||||||
|
and 'maximum' value to enforce a range that's a subset of the Python float
|
||||||
|
implementation. Python floats are doubles.
|
||||||
|
"""
|
||||||
|
minimum = None # type: typing.Optional[float]
|
||||||
|
maximum = None # type: typing.Optional[float]
|
||||||
|
|
||||||
|
def __init__(self, min_value=None, max_value=None):
|
||||||
|
"""
|
||||||
|
A more restrictive minimum or maximum value can be specified than the
|
||||||
|
range inherent to the defined type.
|
||||||
|
"""
|
||||||
|
if min_value is not None:
|
||||||
|
assert isinstance(min_value, numbers.Real), \
|
||||||
|
'min_value must be a real number'
|
||||||
|
if not isinstance(min_value, float):
|
||||||
|
try:
|
||||||
|
min_value = float(min_value)
|
||||||
|
except OverflowError:
|
||||||
|
raise AssertionError('min_value is too small for a float')
|
||||||
|
if self.minimum is not None and min_value < self.minimum:
|
||||||
|
raise AssertionError('min_value cannot be less than the '
|
||||||
|
'minimum value for this type (%f < %f)' %
|
||||||
|
(min_value, self.minimum))
|
||||||
|
self.minimum = min_value
|
||||||
|
if max_value is not None:
|
||||||
|
assert isinstance(max_value, numbers.Real), \
|
||||||
|
'max_value must be a real number'
|
||||||
|
if not isinstance(max_value, float):
|
||||||
|
try:
|
||||||
|
max_value = float(max_value)
|
||||||
|
except OverflowError:
|
||||||
|
raise AssertionError('max_value is too large for a float')
|
||||||
|
if self.maximum is not None and max_value > self.maximum:
|
||||||
|
raise AssertionError('max_value cannot be greater than the '
|
||||||
|
'maximum value for this type (%f < %f)' %
|
||||||
|
(max_value, self.maximum))
|
||||||
|
self.maximum = max_value
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, numbers.Real):
|
||||||
|
raise ValidationError('expected real number, got %s' %
|
||||||
|
generic_type_name(val))
|
||||||
|
if not isinstance(val, float):
|
||||||
|
# This checks for the case where a number is passed in with a
|
||||||
|
# magnitude larger than supported by float64.
|
||||||
|
try:
|
||||||
|
val = float(val)
|
||||||
|
except OverflowError:
|
||||||
|
raise ValidationError('too large for float')
|
||||||
|
if math.isnan(val) or math.isinf(val):
|
||||||
|
raise ValidationError('%f values are not supported' % val)
|
||||||
|
if self.minimum is not None and val < self.minimum:
|
||||||
|
raise ValidationError('%f is not greater than %f' %
|
||||||
|
(val, self.minimum))
|
||||||
|
if self.maximum is not None and val > self.maximum:
|
||||||
|
raise ValidationError('%f is not less than %f' %
|
||||||
|
(val, self.maximum))
|
||||||
|
return val
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '%s()' % self.__class__.__name__
|
||||||
|
|
||||||
|
|
||||||
|
class Float32(Real):
|
||||||
|
# Maximum and minimums from the IEEE 754-1985 standard
|
||||||
|
minimum = -3.40282 * 10**38
|
||||||
|
maximum = 3.40282 * 10**38
|
||||||
|
|
||||||
|
|
||||||
|
class Float64(Real):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class String(Primitive):
|
||||||
|
"""Represents a unicode string."""
|
||||||
|
|
||||||
|
def __init__(self, min_length=None, max_length=None, pattern=None):
|
||||||
|
if min_length is not None:
|
||||||
|
assert isinstance(min_length, numbers.Integral), \
|
||||||
|
'min_length must be an integral number'
|
||||||
|
assert min_length >= 0, 'min_length must be >= 0'
|
||||||
|
if max_length is not None:
|
||||||
|
assert isinstance(max_length, numbers.Integral), \
|
||||||
|
'max_length must be an integral number'
|
||||||
|
assert max_length > 0, 'max_length must be > 0'
|
||||||
|
if min_length and max_length:
|
||||||
|
assert max_length >= min_length, 'max_length must be >= min_length'
|
||||||
|
if pattern is not None:
|
||||||
|
assert isinstance(pattern, six.string_types), \
|
||||||
|
'pattern must be a string'
|
||||||
|
|
||||||
|
self.min_length = min_length
|
||||||
|
self.max_length = max_length
|
||||||
|
self.pattern = pattern
|
||||||
|
self.pattern_re = None
|
||||||
|
|
||||||
|
if pattern:
|
||||||
|
try:
|
||||||
|
self.pattern_re = re.compile(r"\A(?:" + pattern + r")\Z")
|
||||||
|
except re.error as e:
|
||||||
|
raise AssertionError('Regex {!r} failed: {}'.format(
|
||||||
|
pattern, e.args[0]))
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
"""
|
||||||
|
A unicode string of the correct length and pattern will pass validation.
|
||||||
|
In PY2, we enforce that a str type must be valid utf-8, and a unicode
|
||||||
|
string will be returned.
|
||||||
|
"""
|
||||||
|
if not isinstance(val, six.string_types):
|
||||||
|
raise ValidationError("'%s' expected to be a string, got %s"
|
||||||
|
% (val, generic_type_name(val)))
|
||||||
|
if not six.PY3 and isinstance(val, str):
|
||||||
|
try:
|
||||||
|
val = val.decode('utf-8')
|
||||||
|
except UnicodeDecodeError:
|
||||||
|
raise ValidationError("'%s' was not valid utf-8")
|
||||||
|
|
||||||
|
if self.max_length is not None and len(val) > self.max_length:
|
||||||
|
raise ValidationError("'%s' must be at most %d characters, got %d"
|
||||||
|
% (val, self.max_length, len(val)))
|
||||||
|
if self.min_length is not None and len(val) < self.min_length:
|
||||||
|
raise ValidationError("'%s' must be at least %d characters, got %d"
|
||||||
|
% (val, self.min_length, len(val)))
|
||||||
|
|
||||||
|
if self.pattern and not self.pattern_re.match(val):
|
||||||
|
raise ValidationError("'%s' did not match pattern '%s'"
|
||||||
|
% (val, self.pattern))
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class Bytes(Primitive):
|
||||||
|
|
||||||
|
def __init__(self, min_length=None, max_length=None):
|
||||||
|
if min_length is not None:
|
||||||
|
assert isinstance(min_length, numbers.Integral), \
|
||||||
|
'min_length must be an integral number'
|
||||||
|
assert min_length >= 0, 'min_length must be >= 0'
|
||||||
|
if max_length is not None:
|
||||||
|
assert isinstance(max_length, numbers.Integral), \
|
||||||
|
'max_length must be an integral number'
|
||||||
|
assert max_length > 0, 'max_length must be > 0'
|
||||||
|
if min_length is not None and max_length is not None:
|
||||||
|
assert max_length >= min_length, 'max_length must be >= min_length'
|
||||||
|
|
||||||
|
self.min_length = min_length
|
||||||
|
self.max_length = max_length
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, _binary_types):
|
||||||
|
raise ValidationError("expected bytes type, got %s"
|
||||||
|
% generic_type_name(val))
|
||||||
|
elif self.max_length is not None and len(val) > self.max_length:
|
||||||
|
raise ValidationError("'%s' must have at most %d bytes, got %d"
|
||||||
|
% (val, self.max_length, len(val)))
|
||||||
|
elif self.min_length is not None and len(val) < self.min_length:
|
||||||
|
raise ValidationError("'%s' has fewer than %d bytes, got %d"
|
||||||
|
% (val, self.min_length, len(val)))
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class Timestamp(Primitive):
|
||||||
|
"""Note that while a format is specified, it isn't used in validation
|
||||||
|
since a native Python datetime object is preferred. The format, however,
|
||||||
|
can and should be used by serializers."""
|
||||||
|
|
||||||
|
def __init__(self, fmt):
|
||||||
|
"""fmt must be composed of format codes that the C standard (1989)
|
||||||
|
supports, most notably in its strftime() function."""
|
||||||
|
assert isinstance(fmt, six.text_type), 'format must be a string'
|
||||||
|
self.format = fmt
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, datetime.datetime):
|
||||||
|
raise ValidationError('expected timestamp, got %s'
|
||||||
|
% generic_type_name(val))
|
||||||
|
elif val.tzinfo is not None and \
|
||||||
|
val.tzinfo.utcoffset(val).total_seconds() != 0:
|
||||||
|
raise ValidationError('timestamp should have either a UTC '
|
||||||
|
'timezone or none set at all')
|
||||||
|
return val
|
||||||
|
|
||||||
|
|
||||||
|
class Composite(Validator):
|
||||||
|
"""Validator for a type that builds on other primitive and composite
|
||||||
|
types."""
|
||||||
|
# pylint: disable=abstract-method
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class List(Composite):
|
||||||
|
"""Assumes list contents are homogeneous with respect to types."""
|
||||||
|
|
||||||
|
def __init__(self, item_validator, min_items=None, max_items=None):
|
||||||
|
"""Every list item will be validated with item_validator."""
|
||||||
|
self.item_validator = item_validator
|
||||||
|
if min_items is not None:
|
||||||
|
assert isinstance(min_items, numbers.Integral), \
|
||||||
|
'min_items must be an integral number'
|
||||||
|
assert min_items >= 0, 'min_items must be >= 0'
|
||||||
|
if max_items is not None:
|
||||||
|
assert isinstance(max_items, numbers.Integral), \
|
||||||
|
'max_items must be an integral number'
|
||||||
|
assert max_items > 0, 'max_items must be > 0'
|
||||||
|
if min_items is not None and max_items is not None:
|
||||||
|
assert max_items >= min_items, 'max_items must be >= min_items'
|
||||||
|
|
||||||
|
self.min_items = min_items
|
||||||
|
self.max_items = max_items
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, (tuple, list)):
|
||||||
|
raise ValidationError('%r is not a valid list' % val)
|
||||||
|
elif self.max_items is not None and len(val) > self.max_items:
|
||||||
|
raise ValidationError('%r has more than %s items'
|
||||||
|
% (val, self.max_items))
|
||||||
|
elif self.min_items is not None and len(val) < self.min_items:
|
||||||
|
raise ValidationError('%r has fewer than %s items'
|
||||||
|
% (val, self.min_items))
|
||||||
|
return [self.item_validator.validate(item) for item in val]
|
||||||
|
|
||||||
|
|
||||||
|
class Map(Composite):
|
||||||
|
"""Assumes map keys and values are homogeneous with respect to types."""
|
||||||
|
|
||||||
|
def __init__(self, key_validator, value_validator):
|
||||||
|
"""
|
||||||
|
Every Map key/value pair will be validated with item_validator.
|
||||||
|
key validators must be a subclass of a String validator
|
||||||
|
"""
|
||||||
|
self.key_validator = key_validator
|
||||||
|
self.value_validator = value_validator
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if not isinstance(val, dict):
|
||||||
|
raise ValidationError('%r is not a valid dict' % val)
|
||||||
|
return {
|
||||||
|
self.key_validator.validate(key):
|
||||||
|
self.value_validator.validate(value) for key, value in val.items()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class Struct(Composite):
|
||||||
|
|
||||||
|
def __init__(self, definition):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
definition (class): A generated class representing a Stone struct
|
||||||
|
from a spec. Must have a _fields_ attribute with the following
|
||||||
|
structure:
|
||||||
|
|
||||||
|
_fields_ = [(field_name, validator), ...]
|
||||||
|
|
||||||
|
where
|
||||||
|
field_name: Name of the field (str).
|
||||||
|
validator: Validator object.
|
||||||
|
"""
|
||||||
|
super(Struct, self).__init__()
|
||||||
|
self.definition = definition
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
"""
|
||||||
|
For a val to pass validation, val must be of the correct type and have
|
||||||
|
all required fields present.
|
||||||
|
"""
|
||||||
|
self.validate_type_only(val)
|
||||||
|
self.validate_fields_only(val)
|
||||||
|
return val
|
||||||
|
|
||||||
|
def validate_with_permissions(self, val, caller_permissions):
|
||||||
|
"""
|
||||||
|
For a val to pass validation, val must be of the correct type and have
|
||||||
|
all required permissioned fields present. Should only be called
|
||||||
|
for callers with extra permissions.
|
||||||
|
"""
|
||||||
|
self.validate(val)
|
||||||
|
self.validate_fields_only_with_permissions(val, caller_permissions)
|
||||||
|
return val
|
||||||
|
|
||||||
|
def validate_fields_only(self, val):
|
||||||
|
"""
|
||||||
|
To pass field validation, no required field should be missing.
|
||||||
|
|
||||||
|
This method assumes that the contents of each field have already been
|
||||||
|
validated on assignment, so it's merely a presence check.
|
||||||
|
|
||||||
|
FIXME(kelkabany): Since the definition object does not maintain a list
|
||||||
|
of which fields are required, all fields are scanned.
|
||||||
|
"""
|
||||||
|
for field_name in self.definition._all_field_names_:
|
||||||
|
if not hasattr(val, field_name):
|
||||||
|
raise ValidationError("missing required field '%s'" %
|
||||||
|
field_name)
|
||||||
|
|
||||||
|
def validate_fields_only_with_permissions(self, val, caller_permissions):
|
||||||
|
"""
|
||||||
|
To pass field validation, no required field should be missing.
|
||||||
|
This method assumes that the contents of each field have already been
|
||||||
|
validated on assignment, so it's merely a presence check.
|
||||||
|
Should only be called for callers with extra permissions.
|
||||||
|
"""
|
||||||
|
self.validate_fields_only(val)
|
||||||
|
|
||||||
|
# check if type has been patched
|
||||||
|
for extra_permission in caller_permissions.permissions:
|
||||||
|
all_field_names = '_all_{}_field_names_'.format(extra_permission)
|
||||||
|
for field_name in getattr(self.definition, all_field_names, set()):
|
||||||
|
if not hasattr(val, field_name):
|
||||||
|
raise ValidationError("missing required field '%s'" % field_name)
|
||||||
|
|
||||||
|
def validate_type_only(self, val):
|
||||||
|
"""
|
||||||
|
Use this when you only want to validate that the type of an object
|
||||||
|
is correct, but not yet validate each field.
|
||||||
|
"""
|
||||||
|
# Since the definition maintains the list of fields for serialization,
|
||||||
|
# we're okay with a subclass that might have extra information. This
|
||||||
|
# makes it easier to return one subclass for two routes, one of which
|
||||||
|
# relies on the parent class.
|
||||||
|
if not isinstance(val, self.definition):
|
||||||
|
raise ValidationError('expected type %s, got %s' %
|
||||||
|
(self.definition.__name__, generic_type_name(val)))
|
||||||
|
|
||||||
|
def has_default(self):
|
||||||
|
return not self.definition._has_required_fields
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
assert not self.definition._has_required_fields, 'No default available.'
|
||||||
|
return self.definition()
|
||||||
|
|
||||||
|
|
||||||
|
class StructTree(Struct):
|
||||||
|
"""Validator for structs with enumerated subtypes.
|
||||||
|
|
||||||
|
NOTE: validate_fields_only() validates the fields known to this base
|
||||||
|
struct, but does not do any validation specific to the subtype.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# See PyCQA/pylint#1043 for why this is disabled; this should show up
|
||||||
|
# as a usless-suppression (and can be removed) once a fix is released
|
||||||
|
def __init__(self, definition): # pylint: disable=useless-super-delegation
|
||||||
|
super(StructTree, self).__init__(definition)
|
||||||
|
|
||||||
|
|
||||||
|
class Union(Composite):
|
||||||
|
|
||||||
|
def __init__(self, definition):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
definition (class): A generated class representing a Stone union
|
||||||
|
from a spec. Must have a _tagmap attribute with the following
|
||||||
|
structure:
|
||||||
|
|
||||||
|
_tagmap = {field_name: validator, ...}
|
||||||
|
|
||||||
|
where
|
||||||
|
field_name (str): Tag name.
|
||||||
|
validator (Validator): Tag value validator.
|
||||||
|
"""
|
||||||
|
self.definition = definition
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
"""
|
||||||
|
For a val to pass validation, it must have a _tag set. This assumes
|
||||||
|
that the object validated that _tag is a valid tag, and that any
|
||||||
|
associated value has also been validated.
|
||||||
|
"""
|
||||||
|
self.validate_type_only(val)
|
||||||
|
if not hasattr(val, '_tag') or val._tag is None:
|
||||||
|
raise ValidationError('no tag set')
|
||||||
|
return val
|
||||||
|
|
||||||
|
def validate_type_only(self, val):
|
||||||
|
"""
|
||||||
|
Use this when you only want to validate that the type of an object
|
||||||
|
is correct, but not yet validate each field.
|
||||||
|
|
||||||
|
We check whether val is a Python parent class of the definition. This
|
||||||
|
is because Union subtyping works in the opposite direction of Python
|
||||||
|
inheritance. For example, if a union U2 extends U1 in Python, this
|
||||||
|
validator will accept U1 in places where U2 is expected.
|
||||||
|
"""
|
||||||
|
if not issubclass(self.definition, type(val)):
|
||||||
|
raise ValidationError('expected type %s or subtype, got %s' %
|
||||||
|
(self.definition.__name__, generic_type_name(val)))
|
||||||
|
|
||||||
|
|
||||||
|
class Void(Primitive):
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if val is not None:
|
||||||
|
raise ValidationError('expected NoneType, got %s' %
|
||||||
|
generic_type_name(val))
|
||||||
|
|
||||||
|
def has_default(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Nullable(Validator):
|
||||||
|
|
||||||
|
def __init__(self, validator):
|
||||||
|
assert isinstance(validator, (Primitive, Composite)), \
|
||||||
|
'validator must be for a primitive or composite type'
|
||||||
|
assert not isinstance(validator, Nullable), \
|
||||||
|
'nullables cannot be stacked'
|
||||||
|
assert not isinstance(validator, Void), \
|
||||||
|
'void cannot be made nullable'
|
||||||
|
self.validator = validator
|
||||||
|
|
||||||
|
def validate(self, val):
|
||||||
|
if val is None:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return self.validator.validate(val)
|
||||||
|
|
||||||
|
def validate_type_only(self, val):
|
||||||
|
"""Use this only if Nullable is wrapping a Composite."""
|
||||||
|
if val is None:
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return self.validator.validate_type_only(val)
|
||||||
|
|
||||||
|
def has_default(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_default(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Redactor(object):
|
||||||
|
def __init__(self, regex):
|
||||||
|
"""
|
||||||
|
Args:
|
||||||
|
regex: What parts of the field to redact.
|
||||||
|
"""
|
||||||
|
self.regex = regex
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def apply(self, val):
|
||||||
|
"""Redacts information from annotated field.
|
||||||
|
Returns: A redacted version of the string provided.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_matches(self, val):
|
||||||
|
if not self.regex:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return re.search(self.regex, val)
|
||||||
|
except TypeError:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class HashRedactor(Redactor):
|
||||||
|
def apply(self, val):
|
||||||
|
matches = self._get_matches(val)
|
||||||
|
|
||||||
|
val_to_hash = str(val) if isinstance(val, int) or isinstance(val, float) else val
|
||||||
|
|
||||||
|
try:
|
||||||
|
# add string literal to ensure unicode
|
||||||
|
hashed = hashlib.md5(val_to_hash.encode('utf-8')).hexdigest() + ''
|
||||||
|
except [AttributeError, ValueError]:
|
||||||
|
hashed = None
|
||||||
|
|
||||||
|
if matches:
|
||||||
|
blotted = '***'.join(matches.groups())
|
||||||
|
if hashed:
|
||||||
|
return '{} ({})'.format(hashed, blotted)
|
||||||
|
return blotted
|
||||||
|
return hashed
|
||||||
|
|
||||||
|
|
||||||
|
class BlotRedactor(Redactor):
|
||||||
|
def apply(self, val):
|
||||||
|
matches = self._get_matches(val)
|
||||||
|
if matches:
|
||||||
|
return '***'.join(matches.groups())
|
||||||
|
return '********'
|
19709
resources/lib/dropbox/team.py
Normal file
19709
resources/lib/dropbox/team.py
Normal file
File diff suppressed because it is too large
Load Diff
572
resources/lib/dropbox/team_common.py
Normal file
572
resources/lib/dropbox/team_common.py
Normal file
@ -0,0 +1,572 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Auto-generated by Stone, do not modify.
|
||||||
|
# @generated
|
||||||
|
# flake8: noqa
|
||||||
|
# pylint: skip-file
|
||||||
|
try:
|
||||||
|
from . import stone_validators as bv
|
||||||
|
from . import stone_base as bb
|
||||||
|
except (ImportError, SystemError, ValueError):
|
||||||
|
# Catch errors raised when importing a relative module when not in a package.
|
||||||
|
# This makes testing this file directly (outside of a package) easier.
|
||||||
|
import stone_validators as bv
|
||||||
|
import stone_base as bb
|
||||||
|
|
||||||
|
try:
|
||||||
|
from . import (
|
||||||
|
common,
|
||||||
|
)
|
||||||
|
except (ImportError, SystemError, ValueError):
|
||||||
|
import common
|
||||||
|
|
||||||
|
class GroupManagementType(bb.Union):
|
||||||
|
"""
|
||||||
|
The group type determines how a group is managed.
|
||||||
|
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar team_common.GroupManagementType.user_managed: A group which is managed
|
||||||
|
by selected users.
|
||||||
|
:ivar team_common.GroupManagementType.company_managed: A group which is
|
||||||
|
managed by team admins only.
|
||||||
|
:ivar team_common.GroupManagementType.system_managed: A group which is
|
||||||
|
managed automatically by Dropbox.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
user_managed = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
company_managed = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
system_managed = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_user_managed(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``user_managed``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'user_managed'
|
||||||
|
|
||||||
|
def is_company_managed(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``company_managed``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'company_managed'
|
||||||
|
|
||||||
|
def is_system_managed(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``system_managed``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'system_managed'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(GroupManagementType, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'GroupManagementType(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
GroupManagementType_validator = bv.Union(GroupManagementType)
|
||||||
|
|
||||||
|
class GroupSummary(bb.Struct):
|
||||||
|
"""
|
||||||
|
Information about a group.
|
||||||
|
|
||||||
|
:ivar team_common.GroupSummary.group_external_id: External ID of group. This
|
||||||
|
is an arbitrary ID that an admin can attach to a group.
|
||||||
|
:ivar team_common.GroupSummary.member_count: The number of members in the
|
||||||
|
group.
|
||||||
|
:ivar team_common.GroupSummary.group_management_type: Who is allowed to
|
||||||
|
manage the group.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_group_name_value',
|
||||||
|
'_group_name_present',
|
||||||
|
'_group_id_value',
|
||||||
|
'_group_id_present',
|
||||||
|
'_group_external_id_value',
|
||||||
|
'_group_external_id_present',
|
||||||
|
'_member_count_value',
|
||||||
|
'_member_count_present',
|
||||||
|
'_group_management_type_value',
|
||||||
|
'_group_management_type_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
group_name=None,
|
||||||
|
group_id=None,
|
||||||
|
group_management_type=None,
|
||||||
|
group_external_id=None,
|
||||||
|
member_count=None):
|
||||||
|
self._group_name_value = None
|
||||||
|
self._group_name_present = False
|
||||||
|
self._group_id_value = None
|
||||||
|
self._group_id_present = False
|
||||||
|
self._group_external_id_value = None
|
||||||
|
self._group_external_id_present = False
|
||||||
|
self._member_count_value = None
|
||||||
|
self._member_count_present = False
|
||||||
|
self._group_management_type_value = None
|
||||||
|
self._group_management_type_present = False
|
||||||
|
if group_name is not None:
|
||||||
|
self.group_name = group_name
|
||||||
|
if group_id is not None:
|
||||||
|
self.group_id = group_id
|
||||||
|
if group_external_id is not None:
|
||||||
|
self.group_external_id = group_external_id
|
||||||
|
if member_count is not None:
|
||||||
|
self.member_count = member_count
|
||||||
|
if group_management_type is not None:
|
||||||
|
self.group_management_type = group_management_type
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_name(self):
|
||||||
|
"""
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._group_name_present:
|
||||||
|
return self._group_name_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'group_name'")
|
||||||
|
|
||||||
|
@group_name.setter
|
||||||
|
def group_name(self, val):
|
||||||
|
val = self._group_name_validator.validate(val)
|
||||||
|
self._group_name_value = val
|
||||||
|
self._group_name_present = True
|
||||||
|
|
||||||
|
@group_name.deleter
|
||||||
|
def group_name(self):
|
||||||
|
self._group_name_value = None
|
||||||
|
self._group_name_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_id(self):
|
||||||
|
"""
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._group_id_present:
|
||||||
|
return self._group_id_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'group_id'")
|
||||||
|
|
||||||
|
@group_id.setter
|
||||||
|
def group_id(self, val):
|
||||||
|
val = self._group_id_validator.validate(val)
|
||||||
|
self._group_id_value = val
|
||||||
|
self._group_id_present = True
|
||||||
|
|
||||||
|
@group_id.deleter
|
||||||
|
def group_id(self):
|
||||||
|
self._group_id_value = None
|
||||||
|
self._group_id_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_external_id(self):
|
||||||
|
"""
|
||||||
|
External ID of group. This is an arbitrary ID that an admin can attach
|
||||||
|
to a group.
|
||||||
|
|
||||||
|
:rtype: str
|
||||||
|
"""
|
||||||
|
if self._group_external_id_present:
|
||||||
|
return self._group_external_id_value
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@group_external_id.setter
|
||||||
|
def group_external_id(self, val):
|
||||||
|
if val is None:
|
||||||
|
del self.group_external_id
|
||||||
|
return
|
||||||
|
val = self._group_external_id_validator.validate(val)
|
||||||
|
self._group_external_id_value = val
|
||||||
|
self._group_external_id_present = True
|
||||||
|
|
||||||
|
@group_external_id.deleter
|
||||||
|
def group_external_id(self):
|
||||||
|
self._group_external_id_value = None
|
||||||
|
self._group_external_id_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def member_count(self):
|
||||||
|
"""
|
||||||
|
The number of members in the group.
|
||||||
|
|
||||||
|
:rtype: int
|
||||||
|
"""
|
||||||
|
if self._member_count_present:
|
||||||
|
return self._member_count_value
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@member_count.setter
|
||||||
|
def member_count(self, val):
|
||||||
|
if val is None:
|
||||||
|
del self.member_count
|
||||||
|
return
|
||||||
|
val = self._member_count_validator.validate(val)
|
||||||
|
self._member_count_value = val
|
||||||
|
self._member_count_present = True
|
||||||
|
|
||||||
|
@member_count.deleter
|
||||||
|
def member_count(self):
|
||||||
|
self._member_count_value = None
|
||||||
|
self._member_count_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def group_management_type(self):
|
||||||
|
"""
|
||||||
|
Who is allowed to manage the group.
|
||||||
|
|
||||||
|
:rtype: GroupManagementType
|
||||||
|
"""
|
||||||
|
if self._group_management_type_present:
|
||||||
|
return self._group_management_type_value
|
||||||
|
else:
|
||||||
|
raise AttributeError("missing required field 'group_management_type'")
|
||||||
|
|
||||||
|
@group_management_type.setter
|
||||||
|
def group_management_type(self, val):
|
||||||
|
self._group_management_type_validator.validate_type_only(val)
|
||||||
|
self._group_management_type_value = val
|
||||||
|
self._group_management_type_present = True
|
||||||
|
|
||||||
|
@group_management_type.deleter
|
||||||
|
def group_management_type(self):
|
||||||
|
self._group_management_type_value = None
|
||||||
|
self._group_management_type_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(GroupSummary, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'GroupSummary(group_name={!r}, group_id={!r}, group_management_type={!r}, group_external_id={!r}, member_count={!r})'.format(
|
||||||
|
self._group_name_value,
|
||||||
|
self._group_id_value,
|
||||||
|
self._group_management_type_value,
|
||||||
|
self._group_external_id_value,
|
||||||
|
self._member_count_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
GroupSummary_validator = bv.Struct(GroupSummary)
|
||||||
|
|
||||||
|
class GroupType(bb.Union):
|
||||||
|
"""
|
||||||
|
The group type determines how a group is created and managed.
|
||||||
|
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar team_common.GroupType.team: A group to which team members are
|
||||||
|
automatically added. Applicable to `team folders
|
||||||
|
<https://www.dropbox.com/help/986>`_ only.
|
||||||
|
:ivar team_common.GroupType.user_managed: A group is created and managed by
|
||||||
|
a user.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
team = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
user_managed = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_team(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``team``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'team'
|
||||||
|
|
||||||
|
def is_user_managed(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``user_managed``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'user_managed'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(GroupType, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'GroupType(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
GroupType_validator = bv.Union(GroupType)
|
||||||
|
|
||||||
|
class MemberSpaceLimitType(bb.Union):
|
||||||
|
"""
|
||||||
|
The type of the space limit imposed on a team member.
|
||||||
|
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar team_common.MemberSpaceLimitType.off: The team member does not have
|
||||||
|
imposed space limit.
|
||||||
|
:ivar team_common.MemberSpaceLimitType.alert_only: The team member has soft
|
||||||
|
imposed space limit - the limit is used for display and for
|
||||||
|
notifications.
|
||||||
|
:ivar team_common.MemberSpaceLimitType.stop_sync: The team member has hard
|
||||||
|
imposed space limit - Dropbox file sync will stop after the limit is
|
||||||
|
reached.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = 'other'
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
off = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
alert_only = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
stop_sync = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
other = None
|
||||||
|
|
||||||
|
def is_off(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``off``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'off'
|
||||||
|
|
||||||
|
def is_alert_only(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``alert_only``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'alert_only'
|
||||||
|
|
||||||
|
def is_stop_sync(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``stop_sync``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'stop_sync'
|
||||||
|
|
||||||
|
def is_other(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``other``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'other'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(MemberSpaceLimitType, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'MemberSpaceLimitType(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
MemberSpaceLimitType_validator = bv.Union(MemberSpaceLimitType)
|
||||||
|
|
||||||
|
class TimeRange(bb.Struct):
|
||||||
|
"""
|
||||||
|
Time range.
|
||||||
|
|
||||||
|
:ivar team_common.TimeRange.start_time: Optional starting time (inclusive).
|
||||||
|
:ivar team_common.TimeRange.end_time: Optional ending time (exclusive).
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = [
|
||||||
|
'_start_time_value',
|
||||||
|
'_start_time_present',
|
||||||
|
'_end_time_value',
|
||||||
|
'_end_time_present',
|
||||||
|
]
|
||||||
|
|
||||||
|
_has_required_fields = False
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
start_time=None,
|
||||||
|
end_time=None):
|
||||||
|
self._start_time_value = None
|
||||||
|
self._start_time_present = False
|
||||||
|
self._end_time_value = None
|
||||||
|
self._end_time_present = False
|
||||||
|
if start_time is not None:
|
||||||
|
self.start_time = start_time
|
||||||
|
if end_time is not None:
|
||||||
|
self.end_time = end_time
|
||||||
|
|
||||||
|
@property
|
||||||
|
def start_time(self):
|
||||||
|
"""
|
||||||
|
Optional starting time (inclusive).
|
||||||
|
|
||||||
|
:rtype: datetime.datetime
|
||||||
|
"""
|
||||||
|
if self._start_time_present:
|
||||||
|
return self._start_time_value
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@start_time.setter
|
||||||
|
def start_time(self, val):
|
||||||
|
if val is None:
|
||||||
|
del self.start_time
|
||||||
|
return
|
||||||
|
val = self._start_time_validator.validate(val)
|
||||||
|
self._start_time_value = val
|
||||||
|
self._start_time_present = True
|
||||||
|
|
||||||
|
@start_time.deleter
|
||||||
|
def start_time(self):
|
||||||
|
self._start_time_value = None
|
||||||
|
self._start_time_present = False
|
||||||
|
|
||||||
|
@property
|
||||||
|
def end_time(self):
|
||||||
|
"""
|
||||||
|
Optional ending time (exclusive).
|
||||||
|
|
||||||
|
:rtype: datetime.datetime
|
||||||
|
"""
|
||||||
|
if self._end_time_present:
|
||||||
|
return self._end_time_value
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
@end_time.setter
|
||||||
|
def end_time(self, val):
|
||||||
|
if val is None:
|
||||||
|
del self.end_time
|
||||||
|
return
|
||||||
|
val = self._end_time_validator.validate(val)
|
||||||
|
self._end_time_value = val
|
||||||
|
self._end_time_present = True
|
||||||
|
|
||||||
|
@end_time.deleter
|
||||||
|
def end_time(self):
|
||||||
|
self._end_time_value = None
|
||||||
|
self._end_time_present = False
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(TimeRange, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'TimeRange(start_time={!r}, end_time={!r})'.format(
|
||||||
|
self._start_time_value,
|
||||||
|
self._end_time_value,
|
||||||
|
)
|
||||||
|
|
||||||
|
TimeRange_validator = bv.Struct(TimeRange)
|
||||||
|
|
||||||
|
GroupExternalId_validator = bv.String()
|
||||||
|
GroupId_validator = bv.String()
|
||||||
|
MemberExternalId_validator = bv.String(max_length=64)
|
||||||
|
ResellerId_validator = bv.String()
|
||||||
|
TeamMemberId_validator = bv.String()
|
||||||
|
GroupManagementType._user_managed_validator = bv.Void()
|
||||||
|
GroupManagementType._company_managed_validator = bv.Void()
|
||||||
|
GroupManagementType._system_managed_validator = bv.Void()
|
||||||
|
GroupManagementType._other_validator = bv.Void()
|
||||||
|
GroupManagementType._tagmap = {
|
||||||
|
'user_managed': GroupManagementType._user_managed_validator,
|
||||||
|
'company_managed': GroupManagementType._company_managed_validator,
|
||||||
|
'system_managed': GroupManagementType._system_managed_validator,
|
||||||
|
'other': GroupManagementType._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupManagementType.user_managed = GroupManagementType('user_managed')
|
||||||
|
GroupManagementType.company_managed = GroupManagementType('company_managed')
|
||||||
|
GroupManagementType.system_managed = GroupManagementType('system_managed')
|
||||||
|
GroupManagementType.other = GroupManagementType('other')
|
||||||
|
|
||||||
|
GroupSummary._group_name_validator = bv.String()
|
||||||
|
GroupSummary._group_id_validator = GroupId_validator
|
||||||
|
GroupSummary._group_external_id_validator = bv.Nullable(GroupExternalId_validator)
|
||||||
|
GroupSummary._member_count_validator = bv.Nullable(bv.UInt32())
|
||||||
|
GroupSummary._group_management_type_validator = GroupManagementType_validator
|
||||||
|
GroupSummary._all_field_names_ = set([
|
||||||
|
'group_name',
|
||||||
|
'group_id',
|
||||||
|
'group_external_id',
|
||||||
|
'member_count',
|
||||||
|
'group_management_type',
|
||||||
|
])
|
||||||
|
GroupSummary._all_fields_ = [
|
||||||
|
('group_name', GroupSummary._group_name_validator),
|
||||||
|
('group_id', GroupSummary._group_id_validator),
|
||||||
|
('group_external_id', GroupSummary._group_external_id_validator),
|
||||||
|
('member_count', GroupSummary._member_count_validator),
|
||||||
|
('group_management_type', GroupSummary._group_management_type_validator),
|
||||||
|
]
|
||||||
|
|
||||||
|
GroupType._team_validator = bv.Void()
|
||||||
|
GroupType._user_managed_validator = bv.Void()
|
||||||
|
GroupType._other_validator = bv.Void()
|
||||||
|
GroupType._tagmap = {
|
||||||
|
'team': GroupType._team_validator,
|
||||||
|
'user_managed': GroupType._user_managed_validator,
|
||||||
|
'other': GroupType._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupType.team = GroupType('team')
|
||||||
|
GroupType.user_managed = GroupType('user_managed')
|
||||||
|
GroupType.other = GroupType('other')
|
||||||
|
|
||||||
|
MemberSpaceLimitType._off_validator = bv.Void()
|
||||||
|
MemberSpaceLimitType._alert_only_validator = bv.Void()
|
||||||
|
MemberSpaceLimitType._stop_sync_validator = bv.Void()
|
||||||
|
MemberSpaceLimitType._other_validator = bv.Void()
|
||||||
|
MemberSpaceLimitType._tagmap = {
|
||||||
|
'off': MemberSpaceLimitType._off_validator,
|
||||||
|
'alert_only': MemberSpaceLimitType._alert_only_validator,
|
||||||
|
'stop_sync': MemberSpaceLimitType._stop_sync_validator,
|
||||||
|
'other': MemberSpaceLimitType._other_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
MemberSpaceLimitType.off = MemberSpaceLimitType('off')
|
||||||
|
MemberSpaceLimitType.alert_only = MemberSpaceLimitType('alert_only')
|
||||||
|
MemberSpaceLimitType.stop_sync = MemberSpaceLimitType('stop_sync')
|
||||||
|
MemberSpaceLimitType.other = MemberSpaceLimitType('other')
|
||||||
|
|
||||||
|
TimeRange._start_time_validator = bv.Nullable(common.DropboxTimestamp_validator)
|
||||||
|
TimeRange._end_time_validator = bv.Nullable(common.DropboxTimestamp_validator)
|
||||||
|
TimeRange._all_field_names_ = set([
|
||||||
|
'start_time',
|
||||||
|
'end_time',
|
||||||
|
])
|
||||||
|
TimeRange._all_fields_ = [
|
||||||
|
('start_time', TimeRange._start_time_validator),
|
||||||
|
('end_time', TimeRange._end_time_validator),
|
||||||
|
]
|
||||||
|
|
||||||
|
ROUTES = {
|
||||||
|
}
|
||||||
|
|
78874
resources/lib/dropbox/team_log.py
Normal file
78874
resources/lib/dropbox/team_log.py
Normal file
File diff suppressed because it is too large
Load Diff
1661
resources/lib/dropbox/team_policies.py
Normal file
1661
resources/lib/dropbox/team_policies.py
Normal file
File diff suppressed because it is too large
Load Diff
1396
resources/lib/dropbox/trusted-certs.crt
Normal file
1396
resources/lib/dropbox/trusted-certs.crt
Normal file
File diff suppressed because it is too large
Load Diff
1881
resources/lib/dropbox/users.py
Normal file
1881
resources/lib/dropbox/users.py
Normal file
File diff suppressed because it is too large
Load Diff
88
resources/lib/dropbox/users_common.py
Normal file
88
resources/lib/dropbox/users_common.py
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
# Auto-generated by Stone, do not modify.
|
||||||
|
# @generated
|
||||||
|
# flake8: noqa
|
||||||
|
# pylint: skip-file
|
||||||
|
"""
|
||||||
|
This namespace contains common data types used within the users namespace.
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
from . import stone_validators as bv
|
||||||
|
from . import stone_base as bb
|
||||||
|
except (ImportError, SystemError, ValueError):
|
||||||
|
# Catch errors raised when importing a relative module when not in a package.
|
||||||
|
# This makes testing this file directly (outside of a package) easier.
|
||||||
|
import stone_validators as bv
|
||||||
|
import stone_base as bb
|
||||||
|
|
||||||
|
class AccountType(bb.Union):
|
||||||
|
"""
|
||||||
|
What type of account this user has.
|
||||||
|
|
||||||
|
This class acts as a tagged union. Only one of the ``is_*`` methods will
|
||||||
|
return true. To get the associated value of a tag (if one exists), use the
|
||||||
|
corresponding ``get_*`` method.
|
||||||
|
|
||||||
|
:ivar users_common.AccountType.basic: The basic account type.
|
||||||
|
:ivar users_common.AccountType.pro: The Dropbox Pro account type.
|
||||||
|
:ivar users_common.AccountType.business: The Dropbox Business account type.
|
||||||
|
"""
|
||||||
|
|
||||||
|
_catch_all = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
basic = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
pro = None
|
||||||
|
# Attribute is overwritten below the class definition
|
||||||
|
business = None
|
||||||
|
|
||||||
|
def is_basic(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``basic``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'basic'
|
||||||
|
|
||||||
|
def is_pro(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``pro``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'pro'
|
||||||
|
|
||||||
|
def is_business(self):
|
||||||
|
"""
|
||||||
|
Check if the union tag is ``business``.
|
||||||
|
|
||||||
|
:rtype: bool
|
||||||
|
"""
|
||||||
|
return self._tag == 'business'
|
||||||
|
|
||||||
|
def _process_custom_annotations(self, annotation_type, field_path, processor):
|
||||||
|
super(AccountType, self)._process_custom_annotations(annotation_type, field_path, processor)
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'AccountType(%r, %r)' % (self._tag, self._value)
|
||||||
|
|
||||||
|
AccountType_validator = bv.Union(AccountType)
|
||||||
|
|
||||||
|
AccountId_validator = bv.String(min_length=40, max_length=40)
|
||||||
|
AccountType._basic_validator = bv.Void()
|
||||||
|
AccountType._pro_validator = bv.Void()
|
||||||
|
AccountType._business_validator = bv.Void()
|
||||||
|
AccountType._tagmap = {
|
||||||
|
'basic': AccountType._basic_validator,
|
||||||
|
'pro': AccountType._pro_validator,
|
||||||
|
'business': AccountType._business_validator,
|
||||||
|
}
|
||||||
|
|
||||||
|
AccountType.basic = AccountType('basic')
|
||||||
|
AccountType.pro = AccountType('pro')
|
||||||
|
AccountType.business = AccountType('business')
|
||||||
|
|
||||||
|
ROUTES = {
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user