python-documentcloud 4.1.3__py2.py3-none-any.whl → 4.3.0__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
documentcloud/client.py CHANGED
@@ -1,55 +1,48 @@
1
- """
2
- The public interface for the DocumentCloud API
3
- """
4
-
1
+ # Import SquareletClient from python-squarelet
5
2
  # Standard Library
6
3
  import logging
7
- from functools import partial
8
- from urllib.parse import parse_qs, urlparse
9
4
 
10
5
  # Third Party
11
- import ratelimit
12
- import requests
6
+ from squarelet import SquareletClient
13
7
 
14
8
  # Local
15
- from .constants import AUTH_URI, BASE_URI, RATE_LIMIT, RATE_PERIOD, TIMEOUT
9
+ # Local Imports
16
10
  from .documents import DocumentClient
17
- from .exceptions import APIError, CredentialsFailedError, DoesNotExistError
18
11
  from .organizations import OrganizationClient
19
12
  from .projects import ProjectClient
20
- from .toolbox import requests_retry_session
21
13
  from .users import UserClient
22
14
 
23
15
  logger = logging.getLogger("documentcloud")
24
16
 
25
-
26
- class DocumentCloud(object):
17
+ class DocumentCloud(SquareletClient):
27
18
  """
28
- The public interface for the DocumentCloud API
19
+ The public interface for the DocumentCloud API, now integrated with SquareletClient
29
20
  """
30
-
21
+ # pylint:disable=too-many-positional-arguments
31
22
  def __init__(
32
23
  self,
33
24
  username=None,
34
25
  password=None,
35
- base_uri=BASE_URI,
36
- auth_uri=AUTH_URI,
37
- timeout=TIMEOUT,
26
+ base_uri="https://api.www.documentcloud.org/api/",
27
+ auth_uri="https://accounts.muckrock.com/api/",
28
+ timeout=20,
38
29
  loglevel=None,
39
30
  rate_limit=True,
40
31
  rate_limit_sleep=True,
41
32
  ):
42
- self.base_uri = base_uri
43
- self.auth_uri = auth_uri
44
- self.username = username
45
- self.password = password
46
- self._user_id = None
47
- self.timeout = timeout
48
- self.refresh_token = None
49
- self.session = requests.Session()
50
- self._set_tokens()
33
+ # Initialize SquareletClient for authentication and request handling
34
+ super().__init__(
35
+ base_uri=base_uri,
36
+ username=username,
37
+ password=password,
38
+ auth_uri=auth_uri,
39
+ timeout=timeout,
40
+ rate_limit=rate_limit,
41
+ rate_limit_sleep=rate_limit_sleep
42
+ )
51
43
 
52
- if loglevel: # pragma: no cover
44
+ # Set up logging
45
+ if loglevel:
53
46
  logging.basicConfig(
54
47
  level=loglevel,
55
48
  format="%(asctime)s %(levelname)-8s %(name)-25s %(message)s",
@@ -57,121 +50,13 @@ class DocumentCloud(object):
57
50
  else:
58
51
  logger.addHandler(logging.NullHandler())
59
52
 
53
+ # Initialize the sub-clients using SquareletClient
60
54
  self.documents = DocumentClient(self)
61
55
  self.projects = ProjectClient(self)
62
56
  self.users = UserClient(self)
63
57
  self.organizations = OrganizationClient(self)
64
58
 
65
- if rate_limit:
66
- self._request = ratelimit.limits(calls=RATE_LIMIT, period=RATE_PERIOD)(
67
- self._request
68
- )
69
- if rate_limit_sleep:
70
- self._request = ratelimit.sleep_and_retry(self._request)
71
-
72
- def _set_tokens(self):
73
- """Set the refresh and access tokens"""
74
- if self.refresh_token:
75
- access_token, self.refresh_token = self._refresh_tokens(self.refresh_token)
76
- elif self.username and self.password:
77
- access_token, self.refresh_token = self._get_tokens(
78
- self.username, self.password
79
- )
80
- else:
81
- access_token = None
82
-
83
- if access_token:
84
- self.session.headers.update({"Authorization": f"Bearer {access_token}"})
85
-
86
- def _get_tokens(self, username, password):
87
- """Get an access and refresh token in exchange for the username and password"""
88
- response = requests_retry_session().post(
89
- f"{self.auth_uri}token/",
90
- json={"username": username, "password": password},
91
- timeout=self.timeout,
92
- )
93
-
94
- if response.status_code == requests.codes.UNAUTHORIZED:
95
- raise CredentialsFailedError("The username and password are incorrect")
96
-
97
- self.raise_for_status(response)
98
-
99
- json = response.json()
100
- return (json["access"], json["refresh"])
101
-
102
- def _refresh_tokens(self, refresh_token):
103
- """Refresh the access and refresh tokens"""
104
- response = requests_retry_session().post(
105
- f"{self.auth_uri}refresh/",
106
- json={"refresh": refresh_token},
107
- timeout=self.timeout,
108
- )
109
-
110
- if response.status_code == requests.codes.UNAUTHORIZED:
111
- # refresh token is expired
112
- return self._get_tokens(self.username, self.password)
113
-
114
- self.raise_for_status(response)
115
-
116
- json = response.json()
117
- return (json["access"], json["refresh"])
118
-
119
- @property
120
- def user_id(self):
121
- if self._user_id is None:
122
- user = self.users.get("me")
123
- self._user_id = user.id
124
- return self._user_id
125
-
126
- def _request(self, method, url, raise_error=True, **kwargs):
127
- """Generic method to make API requests"""
128
- # pylint: disable=method-hidden
129
- logger.info("request: %s - %s - %s", method, url, kwargs)
130
- set_tokens = kwargs.pop("set_tokens", True)
131
- full_url = kwargs.pop("full_url", False)
132
-
133
- if not full_url:
134
- url = f"{self.base_uri}{url}"
135
-
136
- # set the API to version 2.0
137
- parsed_url = urlparse(url)
138
- if "version" not in parse_qs(parsed_url.query):
139
- # check to avoid double setting version
140
- kwargs.setdefault("params", {}).update({"version": "2.0"})
141
-
142
- response = requests_retry_session(session=self.session).request(
143
- method, url, timeout=self.timeout, **kwargs
144
- )
145
- logger.debug("response: %s - %s", response.status_code, response.content)
146
- if (
147
- response.status_code in [requests.codes.FORBIDDEN, requests.codes.TOO_MANY]
148
- and set_tokens
149
- ):
150
- self._set_tokens()
151
- # track set_tokens to not enter an infinite loop
152
- kwargs["set_tokens"] = False
153
- return self._request(method, url, full_url=True, **kwargs)
154
-
155
- if raise_error:
156
- self.raise_for_status(response)
157
-
158
- return response
159
-
160
- def __getattr__(self, attr):
161
- """Generate methods for each HTTP request type"""
162
- methods = ["get", "options", "head", "post", "put", "patch", "delete"]
163
- if attr in methods:
164
- return partial(self._request, attr)
165
- raise AttributeError(
166
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
167
- )
168
-
169
- def raise_for_status(self, response):
170
- """Raise for status with a custom error class"""
171
- try:
172
- response.raise_for_status()
173
- except requests.exceptions.RequestException as exc:
174
- if exc.response.status_code == 404:
175
- raise DoesNotExistError(response=exc.response) from exc
176
- else:
177
- raise APIError(response=exc.response) from exc
59
+ """def _request(self, method, url, raise_error=True, **kwargs):
60
+ Delegates request to the SquareletClient's _request method
61
+ return self.squarelet_client.request(method, url, raise_error, **kwargs)
62
+ """
@@ -8,7 +8,7 @@ RATE_PERIOD = 1
8
8
  SUPPORTED_EXTENSIONS = [
9
9
  ".abw",
10
10
  ".zabw",
11
- ".md",
11
+ ".pmd",
12
12
  ".pm3",
13
13
  ".pm4",
14
14
  ".pm5",
@@ -51,11 +51,16 @@ SUPPORTED_EXTENSIONS = [
51
51
  ".wk4",
52
52
  ".pct",
53
53
  ".mml",
54
+ ".xml",
55
+ ".xls",
56
+ ".xlw",
57
+ ".xlt",
54
58
  ".xls",
55
59
  ".xlw",
56
60
  ".xlt",
57
61
  ".xlsx",
58
62
  ".docx",
63
+ ".xlsx",
59
64
  ".pptx",
60
65
  ".ppt",
61
66
  ".pps",
@@ -97,5 +102,41 @@ SUPPORTED_EXTENSIONS = [
97
102
  ".pcx",
98
103
  ".pcd",
99
104
  ".psd",
105
+ ".txt",
100
106
  ".pdf",
107
+ ".png",
108
+ ".qxp",
109
+ ".wb2",
110
+ ".wq1",
111
+ ".wq2",
112
+ ".svg",
113
+ ".sgv",
114
+ ".602",
115
+ ".txt",
116
+ ".sdc",
117
+ ".vor",
118
+ ".sda",
119
+ ".sdd",
120
+ ".sdp",
121
+ ".vor",
122
+ ".sdw",
123
+ ".sgl",
124
+ ".vor",
125
+ ".sgf",
126
+ ".rlf",
127
+ ".ras",
128
+ ".svm",
129
+ ".slk",
130
+ ".tif",
131
+ ".tiff",
132
+ ".tga",
133
+ ".uof",
134
+ ".uot",
135
+ ".uos",
136
+ ".uop",
137
+ ".wpd",
138
+ ".wps",
139
+ ".xbm",
140
+ ".xpm",
141
+ ".zmf",
101
142
  ]
@@ -2,38 +2,11 @@
2
2
  Custom exceptions for python-documentcloud
3
3
  """
4
4
 
5
-
6
- class DocumentCloudError(Exception):
7
- """Base class for errors for python-documentcloud"""
8
-
9
- def __init__(self, *args, **kwargs):
10
- self.response = kwargs.pop("response", None)
11
- if self.response is not None:
12
- self.error = self.response.text
13
- self.status_code = self.response.status_code
14
- if not args:
15
- args = [f"{self.status_code} - {self.error}"]
16
- else:
17
- self.error = None
18
- self.status_code = None
19
- super().__init__(*args, **kwargs)
20
-
21
-
22
- class DuplicateObjectError(DocumentCloudError):
23
- """Raised when an object is added to a unique list more than once"""
24
-
25
-
26
- class CredentialsFailedError(DocumentCloudError):
27
- """Raised if unable to obtain an access token due to bad login credentials"""
28
-
29
-
30
- class APIError(DocumentCloudError):
31
- """Any other error calling the API"""
32
-
33
-
34
- class DoesNotExistError(APIError):
35
- """Raised when the user asks the API for something it cannot find"""
36
-
37
-
38
- class MultipleObjectsReturnedError(APIError):
39
- """Raised when the API returns multiple objects when it expected one"""
5
+ # pylint: disable=unused-import
6
+ # Import exceptions from python-squarelet
7
+ from squarelet.exceptions import SquareletError as DocumentCloudError
8
+ from squarelet.exceptions import DuplicateObjectError
9
+ from squarelet.exceptions import CredentialsFailedError
10
+ from squarelet.exceptions import APIError
11
+ from squarelet.exceptions import DoesNotExistError
12
+ from squarelet.exceptions import MultipleObjectsReturnedError
@@ -1,12 +1,11 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: python-documentcloud
3
- Version: 4.1.3
3
+ Version: 4.3.0
4
4
  Summary: A simple Python wrapper for the DocumentCloud API
5
5
  Home-page: https://github.com/muckrock/python-documentcloud
6
6
  Author: Mitchell Kotler
7
7
  Author-email: mitch@muckrock.com
8
8
  License: MIT
9
- Platform: UNKNOWN
10
9
  Classifier: Development Status :: 5 - Production/Stable
11
10
  Classifier: Intended Audience :: Developers
12
11
  Classifier: Operating System :: OS Independent
@@ -20,26 +19,27 @@ Classifier: Programming Language :: Python :: 3.12
20
19
  Classifier: Topic :: Internet :: WWW/HTTP
21
20
  Description-Content-Type: text/markdown
22
21
  License-File: LICENSE
23
- Requires-Dist: fastjsonschema
24
22
  Requires-Dist: future
25
- Requires-Dist: listcrunch >=1.0.1
23
+ Requires-Dist: listcrunch>=1.0.1
26
24
  Requires-Dist: python-dateutil
27
- Requires-Dist: pyyaml
28
25
  Requires-Dist: ratelimit
29
26
  Requires-Dist: requests
30
27
  Requires-Dist: urllib3
28
+ Requires-Dist: pyyaml
29
+ Requires-Dist: fastjsonschema
30
+ Requires-Dist: python-squarelet
31
31
  Provides-Extra: dev
32
- Requires-Dist: black ; extra == 'dev'
33
- Requires-Dist: coverage ; extra == 'dev'
34
- Requires-Dist: isort ; extra == 'dev'
35
- Requires-Dist: pylint ; extra == 'dev'
36
- Requires-Dist: sphinx ; extra == 'dev'
37
- Requires-Dist: twine ; extra == 'dev'
32
+ Requires-Dist: black; extra == "dev"
33
+ Requires-Dist: coverage; extra == "dev"
34
+ Requires-Dist: isort; extra == "dev"
35
+ Requires-Dist: pylint; extra == "dev"
36
+ Requires-Dist: sphinx; extra == "dev"
37
+ Requires-Dist: twine; extra == "dev"
38
38
  Provides-Extra: test
39
- Requires-Dist: pytest ; extra == 'test'
40
- Requires-Dist: pytest-mock ; extra == 'test'
41
- Requires-Dist: pytest-recording ; extra == 'test'
42
- Requires-Dist: vcrpy ; extra == 'test'
39
+ Requires-Dist: pytest; extra == "test"
40
+ Requires-Dist: pytest-mock; extra == "test"
41
+ Requires-Dist: pytest-recording; extra == "test"
42
+ Requires-Dist: vcrpy; extra == "test"
43
43
 
44
44
  <pre><code> ____ _ ____ _ _
45
45
  | _ \ ___ ___ _ _ _ __ ___ ___ _ __ | |_ / ___| | ___ _ _ __| |
@@ -66,5 +66,3 @@ Installation is as easy as...
66
66
  ```bash
67
67
  $ pip install python-documentcloud
68
68
  ```
69
-
70
-
@@ -2,17 +2,17 @@ documentcloud/__init__.py,sha256=XAwOR6JYL-flQV_uC616AMA2rYiXTkeogNolqE6LzN4,220
2
2
  documentcloud/addon.py,sha256=3FxQjm26jknjLdd-GuztiZO4Z7NcgXq4WqunE9oh2es,11754
3
3
  documentcloud/annotations.py,sha256=wVe3wYzyTRvc_hJ3r0m6iyDf6WIFlaGcCnyah_r53pg,2538
4
4
  documentcloud/base.py,sha256=pNF45aleYpQ9fj75CiL3c4Ssv6MO1EmdzZ6wBLPKHDg,6545
5
- documentcloud/client.py,sha256=sNmo34u3ruxlPZW-T629BizAB-Ao7Pg6GhU9qKt0cBI,5906
6
- documentcloud/constants.py,sha256=4GuvF140iB3-0lAvyLUVuuVu4PYjqdkHOAn49dEjsbQ,1333
5
+ documentcloud/client.py,sha256=OBL2a-n9RMbQns3dImTWrnrCjyr17KKJuTlY9OU7ztI,1933
6
+ documentcloud/constants.py,sha256=h6NStSkxPrjQ2gzaIlqftCF7tthkRimddOE8SsmlHag,1828
7
7
  documentcloud/documents.py,sha256=4OFcWpLgiAhAKpw7QJmswji8jkq2xk12XypfEKmotSk,19563
8
- documentcloud/exceptions.py,sha256=nLyrg_6KhBotsZp0L6-Mf4A6HYYS60AV2FxSVSq9xAk,1161
8
+ documentcloud/exceptions.py,sha256=AwIJpcylq6sF6oaenrZE6nr2EBuj23nxTOf3z_RwtuE,461
9
9
  documentcloud/organizations.py,sha256=_Ot6MWzoa5JdU3jqedU-0Fec_K8WrgxqdlIp4oIijes,392
10
10
  documentcloud/projects.py,sha256=KuOiw65a-8fdgbjo7BqjbEbWguds8inkhFJZJd578bs,5328
11
11
  documentcloud/sections.py,sha256=cMf973KMvp6fAPSMXCD67L32Pz1_Tfh81oV2q2UQ9Uk,924
12
12
  documentcloud/toolbox.py,sha256=zFZTyOn40YZjBpqa1H3qjpR4C3Wu1X2g72AvH_ljlic,1835
13
13
  documentcloud/users.py,sha256=yydOXoEsfJlYqryZpXQ4G3aeRc5y_QCHqXd0dfF1aIc,354
14
- python_documentcloud-4.1.3.dist-info/LICENSE,sha256=Z1IBhHCzIeGR9F2iHtcLt2I2qoUhJ2pK139CAIAuFgo,1151
15
- python_documentcloud-4.1.3.dist-info/METADATA,sha256=Sn8_TXz8jilL_emfR7jq93FZrXVL0_BIfUTEYbbvp4k,2694
16
- python_documentcloud-4.1.3.dist-info/WHEEL,sha256=DZajD4pwLWue70CAfc7YaxT1wLUciNBvN_TTcvXpltE,110
17
- python_documentcloud-4.1.3.dist-info/top_level.txt,sha256=rzNW2vA9GqU5ipNQYSP1XJQ54ippjKXVIo9oMlM0Tm4,14
18
- python_documentcloud-4.1.3.dist-info/RECORD,,
14
+ python_documentcloud-4.3.0.dist-info/LICENSE,sha256=Z1IBhHCzIeGR9F2iHtcLt2I2qoUhJ2pK139CAIAuFgo,1151
15
+ python_documentcloud-4.3.0.dist-info/METADATA,sha256=IzRfB3f2kt523y3XT4ndU80eQrYmpEpbU2J3m3noYtY,2695
16
+ python_documentcloud-4.3.0.dist-info/WHEEL,sha256=pxeNX5JdtCe58PUSYP9upmc7jdRPgvT0Gm9kb1SHlVw,109
17
+ python_documentcloud-4.3.0.dist-info/top_level.txt,sha256=rzNW2vA9GqU5ipNQYSP1XJQ54ippjKXVIo9oMlM0Tm4,14
18
+ python_documentcloud-4.3.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.43.0)
2
+ Generator: setuptools (75.6.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py2-none-any
5
5
  Tag: py3-none-any