From 594e9c6287d5754026bebed7e3fbcf814113fd1b Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:01:46 -0600
Subject: [PATCH 1/7] Cleanup credits
---
docs/credits.rst | 11 +-
results.txt | 736 -----------------------------------------------
2 files changed, 4 insertions(+), 743 deletions(-)
delete mode 100644 results.txt
diff --git a/docs/credits.rst b/docs/credits.rst
index 9c286dd..e8c4e02 100644
--- a/docs/credits.rst
+++ b/docs/credits.rst
@@ -1,11 +1,8 @@
Credits
-------
-The lead developer of this project is `Ben Welsh `_.
-
-But it would not be possible without:
-
-* `The DocumentCloud team `_.
-* `Chris Amico `_, `Christopher Groskopf `_ and `Mitchell Kotler `_, who broke ground with code that I've adapted.
-* Fixes from friendly people like `Joe Germuska `_, `Shane Shifflet `_ and `Adi Eyal `_.
+This project would not be possible without:
+* `The MuckRock technology team `_,
+* `Ben Welsh `_,
+* Fixes from `Joe Germuska `_, `Shane Shifflet`_,and `Adi Eyal `_.
\ No newline at end of file
diff --git a/results.txt b/results.txt
deleted file mode 100644
index 4f5a3ab..0000000
--- a/results.txt
+++ /dev/null
@@ -1,736 +0,0 @@
-rm -rf tests/cassettes/test_*/*
-rm -rf tests/cassettes/fixtures/*
-pytest --record-mode=all -m "not short"
-============================= test session starts ==============================
-platform linux -- Python 3.11.10, pytest-8.3.4, pluggy-1.5.0
-rootdir: /home/s/dev/python-documentcloud
-configfile: pytest.ini
-plugins: recording-0.13.2
-collected 140 items / 2 deselected / 138 selected
-
-tests/test_annotations.py ....... [ 5%]
-tests/test_base.py .................... [ 19%]
-tests/test_client.py FFEFFF.E.. [ 26%]
-tests/test_documents.py ............................F.F................. [ 61%]
-.....FFF....F.E..E....... [ 79%]
-tests/test_organizarions.py . [ 80%]
-tests/test_projects.py ........F.F...FFFF [ 93%]
-tests/test_sections.py ... [ 95%]
-tests/test_toolbox.py ..... [ 99%]
-tests/test_users.py . [100%]
-
-==================================== ERRORS ====================================
-____________________ ERROR at setup of test_set_tokens_none ____________________
-
- @pytest.fixture
- def public_client():
-> return DocumentCloud(
- base_uri=BASE_URI, auth_uri=AUTH_URI, timeout=TIMEOUT, rate_limit=False
- )
-
-tests/conftest.py:83:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/client.py:34: in __init__
- self.squarelet_client = SquareletClient(
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:49: in __init__
- self._set_tokens()
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-
- def _set_tokens(self):
- """Set the refresh and access tokens"""
- if self.refresh_token:
- self.access_token, self.refresh_token = self._refresh_tokens(
- self.refresh_token
- )
- elif self.username and self.password:
- self.access_token, self.refresh_token = self._get_tokens(
- self.username, self.password
- )
- elif self.access_token:
- pass # Already have access token, do nothing
- else:
-> raise ValueError("No tokens found")
-E ValueError: No tokens found
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:75: ValueError
-____________________ ERROR at setup of test_user_id_public _____________________
-
- @pytest.fixture
- def public_client():
-> return DocumentCloud(
- base_uri=BASE_URI, auth_uri=AUTH_URI, timeout=TIMEOUT, rate_limit=False
- )
-
-tests/conftest.py:83:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/client.py:34: in __init__
- self.squarelet_client = SquareletClient(
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:49: in __init__
- self._set_tokens()
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-
- def _set_tokens(self):
- """Set the refresh and access tokens"""
- if self.refresh_token:
- self.access_token, self.refresh_token = self._refresh_tokens(
- self.refresh_token
- )
- elif self.username and self.password:
- self.access_token, self.refresh_token = self._get_tokens(
- self.username, self.password
- )
- elif self.access_token:
- pass # Already have access token, do nothing
- else:
-> raise ValueError("No tokens found")
-E ValueError: No tokens found
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:75: ValueError
-___________ ERROR at setup of TestDocumentClient.test_public_upload ____________
-
- @pytest.fixture
- def public_client():
-> return DocumentCloud(
- base_uri=BASE_URI, auth_uri=AUTH_URI, timeout=TIMEOUT, rate_limit=False
- )
-
-tests/conftest.py:83:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/client.py:34: in __init__
- self.squarelet_client = SquareletClient(
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:49: in __init__
- self._set_tokens()
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-
- def _set_tokens(self):
- """Set the refresh and access tokens"""
- if self.refresh_token:
- self.access_token, self.refresh_token = self._refresh_tokens(
- self.refresh_token
- )
- elif self.username and self.password:
- self.access_token, self.refresh_token = self._get_tokens(
- self.username, self.password
- )
- elif self.access_token:
- pass # Already have access token, do nothing
- else:
-> raise ValueError("No tokens found")
-E ValueError: No tokens found
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:75: ValueError
-__________ ERROR at setup of TestDocumentClient.test_upload_big_file ___________
-file /home/s/dev/python-documentcloud/tests/test_documents.py, line 190
- def test_upload_big_file(self, client, mocker):
-E fixture 'mocker' not found
-> available fixtures: allowed_hosts, block_network, cache, capfd, capfdbinary, caplog, capsys, capsysbinary, client, default_cassette_name, disable_recording, doctest_namespace, document, document_factory, monkeypatch, project, project_factory, public_client, pytestconfig, rate_client, record_mode, record_property, record_testsuite_property, record_xml_attribute, recwarn, short_client, tmp_path, tmp_path_factory, tmpdir, tmpdir_factory, vcr, vcr_cassette_dir, vcr_config, vcr_markers
-> use 'pytest --fixtures [testpath]' for help on them.
-
-/home/s/dev/python-documentcloud/tests/test_documents.py:190
-=================================== FAILURES ===================================
-_________________________ test_set_tokens_credentials __________________________
-
-client =
-
- def test_set_tokens_credentials(client):
- """Test setting the tokens using credentials"""
- client.refresh_token = None
-> del client.session.headers["Authorization"]
-E AttributeError: 'DocumentCloud' object has no attribute 'session'
-
-tests/test_client.py:21: AttributeError
-___________________________ test_set_tokens_refresh ____________________________
-
-client =
-
- def test_set_tokens_refresh(client):
- """Test setting the tokens using refresh token"""
- # first set tokens sets, refresh token, second one uses it
- client.refresh_token = None
-> del client.session.headers["Authorization"]
-E AttributeError: 'DocumentCloud' object has no attribute 'session'
-
-tests/test_client.py:31: AttributeError
-_______________________________ test_get_tokens ________________________________
-
-client =
-
- def test_get_tokens(client):
- """Test getting access and refresh tokens using valid credentials"""
-> access, refresh = client._get_tokens(client.username, client.password)
-E AttributeError: 'DocumentCloud' object has no attribute '_get_tokens'
-
-tests/test_client.py:47: AttributeError
-_______________________ test_get_tokens_bad_credentials ________________________
-
-client =
-
- def test_get_tokens_bad_credentials(client):
- """Test getting access and refresh tokens using invalid credentials"""
- with pytest.raises(CredentialsFailedError):
-> client._get_tokens(client.username, "foo")
-E AttributeError: 'DocumentCloud' object has no attribute '_get_tokens'
-
-tests/test_client.py:55: AttributeError
-_____________________________ test_refresh_tokens ______________________________
-
-client =
-
- def test_refresh_tokens(client):
- """Test refreshing the tokens"""
-> access, refresh = client._refresh_tokens(client.refresh_token)
-E AttributeError: 'DocumentCloud' object has no attribute '_refresh_tokens'
-
-tests/test_client.py:60: AttributeError
-____________________________ TestDocument.test_user ____________________________
-
-self =
-document =
-
- def test_user(self, document):
- assert document._user is None
-> assert isinstance(document.user, User)
-
-tests/test_documents.py:90:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self = , attr = 'user'
-
- def __getattr__(self, attr):
- """Generate methods for fetching resources"""
- p_image = re.compile(
- r"^get_(?Pthumbnail|small|normal|large)_image_url(?P_list)?$"
- )
- get = attr.startswith("get_")
- url = attr.endswith("_url")
- text = attr.endswith("_text")
- json = attr.endswith(("_json", "_json_text"))
- fmt = "json" if json else "text" if text else None
- # this allows dropping `get_` to act like a property, ie
- # .full_text_url
- if not get and hasattr(self, f"get_{attr}"):
- return getattr(self, f"get_{attr}")()
- # this allows dropping `_url` to fetch the url, ie
- # .get_full_text()
- if not url and hasattr(self, f"{attr}_url"):
- return lambda *a, **k: self._get_url(
- getattr(self, f"{attr}_url")(*a, **k), fmt
- )
- # this genericizes the image sizes
- m_image = p_image.match(attr)
- if m_image and m_image.group("list"):
- return partial(self.get_image_url_list, size=m_image.group("size"))
- if m_image and not m_image.group("list"):
- return partial(self.get_image_url, size=m_image.group("size"))
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'Document' object has no attribute 'user'
-
-documentcloud/documents.py:100: AttributeError
-________________________ TestDocument.test_organization ________________________
-
-self =
-document =
-
- def test_organization(self, document):
- assert document._organization is None
-> assert isinstance(document.organization, Organization)
-
-tests/test_documents.py:100:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self = , attr = 'organization'
-
- def __getattr__(self, attr):
- """Generate methods for fetching resources"""
- p_image = re.compile(
- r"^get_(?Pthumbnail|small|normal|large)_image_url(?P_list)?$"
- )
- get = attr.startswith("get_")
- url = attr.endswith("_url")
- text = attr.endswith("_text")
- json = attr.endswith(("_json", "_json_text"))
- fmt = "json" if json else "text" if text else None
- # this allows dropping `get_` to act like a property, ie
- # .full_text_url
- if not get and hasattr(self, f"get_{attr}"):
- return getattr(self, f"get_{attr}")()
- # this allows dropping `_url` to fetch the url, ie
- # .get_full_text()
- if not url and hasattr(self, f"{attr}_url"):
- return lambda *a, **k: self._get_url(
- getattr(self, f"{attr}_url")(*a, **k), fmt
- )
- # this genericizes the image sizes
- m_image = p_image.match(attr)
- if m_image and m_image.group("list"):
- return partial(self.get_image_url_list, size=m_image.group("size"))
- if m_image and not m_image.group("list"):
- return partial(self.get_image_url, size=m_image.group("size"))
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'Document' object has no attribute 'organization'
-
-documentcloud/documents.py:100: AttributeError
-_____________________ TestDocument.test_attrs[contributor] _____________________
-
-self =
-document = , attr = 'contributor'
-
- @pytest.mark.parametrize(
- "attr",
- [
- "id",
- "access",
- "asset_url",
- "canonical_url",
- "created_at",
- "data",
- "description",
- "edit_access",
- "language",
- "organization_id",
- "page_count",
- "page_spec",
- "projects",
- "related_article",
- "published_url",
- "slug",
- "source",
- "status",
- "title",
- "updated_at",
- "user_id",
- "pages",
- "contributor",
- "contributor_organization",
- "contributor_organization_slug",
- ],
- )
- def test_attrs(self, document, attr):
-> assert getattr(document, attr)
-
-tests/test_documents.py:134:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self = , attr = 'contributor'
-
- def __getattr__(self, attr):
- """Generate methods for fetching resources"""
- p_image = re.compile(
- r"^get_(?Pthumbnail|small|normal|large)_image_url(?P_list)?$"
- )
- get = attr.startswith("get_")
- url = attr.endswith("_url")
- text = attr.endswith("_text")
- json = attr.endswith(("_json", "_json_text"))
- fmt = "json" if json else "text" if text else None
- # this allows dropping `get_` to act like a property, ie
- # .full_text_url
- if not get and hasattr(self, f"get_{attr}"):
- return getattr(self, f"get_{attr}")()
- # this allows dropping `_url` to fetch the url, ie
- # .get_full_text()
- if not url and hasattr(self, f"{attr}_url"):
- return lambda *a, **k: self._get_url(
- getattr(self, f"{attr}_url")(*a, **k), fmt
- )
- # this genericizes the image sizes
- m_image = p_image.match(attr)
- if m_image and m_image.group("list"):
- return partial(self.get_image_url_list, size=m_image.group("size"))
- if m_image and not m_image.group("list"):
- return partial(self.get_image_url, size=m_image.group("size"))
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'Document' object has no attribute 'contributor'
-
-documentcloud/documents.py:100: AttributeError
-______________ TestDocument.test_attrs[contributor_organization] _______________
-
-self =
-document = , attr = 'contributor_organization'
-
- @pytest.mark.parametrize(
- "attr",
- [
- "id",
- "access",
- "asset_url",
- "canonical_url",
- "created_at",
- "data",
- "description",
- "edit_access",
- "language",
- "organization_id",
- "page_count",
- "page_spec",
- "projects",
- "related_article",
- "published_url",
- "slug",
- "source",
- "status",
- "title",
- "updated_at",
- "user_id",
- "pages",
- "contributor",
- "contributor_organization",
- "contributor_organization_slug",
- ],
- )
- def test_attrs(self, document, attr):
-> assert getattr(document, attr)
-
-tests/test_documents.py:134:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self = , attr = 'contributor_organization'
-
- def __getattr__(self, attr):
- """Generate methods for fetching resources"""
- p_image = re.compile(
- r"^get_(?Pthumbnail|small|normal|large)_image_url(?P_list)?$"
- )
- get = attr.startswith("get_")
- url = attr.endswith("_url")
- text = attr.endswith("_text")
- json = attr.endswith(("_json", "_json_text"))
- fmt = "json" if json else "text" if text else None
- # this allows dropping `get_` to act like a property, ie
- # .full_text_url
- if not get and hasattr(self, f"get_{attr}"):
- return getattr(self, f"get_{attr}")()
- # this allows dropping `_url` to fetch the url, ie
- # .get_full_text()
- if not url and hasattr(self, f"{attr}_url"):
- return lambda *a, **k: self._get_url(
- getattr(self, f"{attr}_url")(*a, **k), fmt
- )
- # this genericizes the image sizes
- m_image = p_image.match(attr)
- if m_image and m_image.group("list"):
- return partial(self.get_image_url_list, size=m_image.group("size"))
- if m_image and not m_image.group("list"):
- return partial(self.get_image_url, size=m_image.group("size"))
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'Document' object has no attribute 'contributor_organization'
-
-documentcloud/documents.py:100: AttributeError
-____________ TestDocument.test_attrs[contributor_organization_slug] ____________
-
-self =
-document = , attr = 'contributor_organization_slug'
-
- @pytest.mark.parametrize(
- "attr",
- [
- "id",
- "access",
- "asset_url",
- "canonical_url",
- "created_at",
- "data",
- "description",
- "edit_access",
- "language",
- "organization_id",
- "page_count",
- "page_spec",
- "projects",
- "related_article",
- "published_url",
- "slug",
- "source",
- "status",
- "title",
- "updated_at",
- "user_id",
- "pages",
- "contributor",
- "contributor_organization",
- "contributor_organization_slug",
- ],
- )
- def test_attrs(self, document, attr):
-> assert getattr(document, attr)
-
-tests/test_documents.py:134:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self = , attr = 'contributor_organization_slug'
-
- def __getattr__(self, attr):
- """Generate methods for fetching resources"""
- p_image = re.compile(
- r"^get_(?Pthumbnail|small|normal|large)_image_url(?P_list)?$"
- )
- get = attr.startswith("get_")
- url = attr.endswith("_url")
- text = attr.endswith("_text")
- json = attr.endswith(("_json", "_json_text"))
- fmt = "json" if json else "text" if text else None
- # this allows dropping `get_` to act like a property, ie
- # .full_text_url
- if not get and hasattr(self, f"get_{attr}"):
- return getattr(self, f"get_{attr}")()
- # this allows dropping `_url` to fetch the url, ie
- # .get_full_text()
- if not url and hasattr(self, f"{attr}_url"):
- return lambda *a, **k: self._get_url(
- getattr(self, f"{attr}_url")(*a, **k), fmt
- )
- # this genericizes the image sizes
- m_image = p_image.match(attr)
- if m_image and m_image.group("list"):
- return partial(self.get_image_url_list, size=m_image.group("size"))
- if m_image and not m_image.group("list"):
- return partial(self.get_image_url, size=m_image.group("size"))
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'Document' object has no attribute 'contributor_organization_slug'
-
-documentcloud/documents.py:100: AttributeError
-_________________________ TestDocumentClient.test_list _________________________
-
-self =
-client =
-
- def test_list(self, client):
- # list and all are aliases
- all_documents = client.documents.all()
- my_documents = client.documents.list(user=client.user_id)
-> assert len(list(all_documents)) > len(list(my_documents.results))
-E assert 8 > 8
-E + where 8 = len([, , , , , , ...])
-E + where [, , , , , , ...] = list(, , , , , , , ]>)
-E + and 8 = len([, , , , , , ...])
-E + where [, , , , , , ...] = list([, , , , , , ...])
-E + where [, , , , , , ...] = , , , , , , , ]>.results
-
-tests/test_documents.py:170: AssertionError
-_________________________ TestProjectClient.test_list __________________________
-
-self =
-client =
-
- def test_list(self, client):
- all_projects = client.projects.list()
-> my_projects = client.projects.all()
-
-tests/test_projects.py:68:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/projects.py:100: in all
- return self.list(user=self.client.user_id, **params)
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-attr = 'user_id'
-
- def __getattr__(self, attr):
- """Generate methods for each HTTP request type (GET, POST, etc.)"""
- methods = ["get", "post", "put", "delete", "patch", "head", "options"]
- if attr in methods:
- return partial(self.request, attr)
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'SquareletClient' object has no attribute 'user_id'
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:180: AttributeError
-_______________________ TestProjectClient.test_get_title _______________________
-
-self =
-client =
-project =
-
- def test_get_title(self, client, project):
-> assert client.projects.get(title=project.title)
-
-tests/test_projects.py:78:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/projects.py:115: in get
- return self.get_by_title(title)
-documentcloud/projects.py:122: in get_by_title
- f"{self.api_path}/", params={"title": title, "user": self.client.user_id}
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-attr = 'user_id'
-
- def __getattr__(self, attr):
- """Generate methods for each HTTP request type (GET, POST, etc.)"""
- methods = ["get", "post", "put", "delete", "patch", "head", "options"]
- if attr in methods:
- return partial(self.request, attr)
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'SquareletClient' object has no attribute 'user_id'
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:180: AttributeError
-_____________________ TestProjectClient.test_get_by_title ______________________
-
-self =
-client =
-project =
-
- def test_get_by_title(self, client, project):
-> assert client.projects.get_by_title(project.title)
-
-tests/test_projects.py:92:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/projects.py:122: in get_by_title
- f"{self.api_path}/", params={"title": title, "user": self.client.user_id}
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-attr = 'user_id'
-
- def __getattr__(self, attr):
- """Generate methods for each HTTP request type (GET, POST, etc.)"""
- methods = ["get", "post", "put", "delete", "patch", "head", "options"]
- if attr in methods:
- return partial(self.request, attr)
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'SquareletClient' object has no attribute 'user_id'
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:180: AttributeError
-_________________ TestProjectClient.test_get_by_title_multiple _________________
-
-self =
-client =
-project_factory = .make_project at 0x731b656465c0>
-
- def test_get_by_title_multiple(self, client, project_factory):
- for _ in range(2):
- project_factory(title="Dupe")
- with pytest.raises(MultipleObjectsReturnedError):
-> client.projects.get_by_title("Dupe")
-
-tests/test_projects.py:98:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/projects.py:122: in get_by_title
- f"{self.api_path}/", params={"title": title, "user": self.client.user_id}
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-attr = 'user_id'
-
- def __getattr__(self, attr):
- """Generate methods for each HTTP request type (GET, POST, etc.)"""
- methods = ["get", "post", "put", "delete", "patch", "head", "options"]
- if attr in methods:
- return partial(self.request, attr)
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'SquareletClient' object has no attribute 'user_id'
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:180: AttributeError
-______________ TestProjectClient.test_get_or_create_by_title_get _______________
-
-self =
-client =
-project =
-
- def test_get_or_create_by_title_get(self, client, project):
- title = project.title
-> project, created = client.projects.get_or_create_by_title(title)
-
-tests/test_projects.py:102:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/projects.py:146: in get_or_create_by_title
- project = self.get(title=title)
-documentcloud/projects.py:115: in get
- return self.get_by_title(title)
-documentcloud/projects.py:122: in get_by_title
- f"{self.api_path}/", params={"title": title, "user": self.client.user_id}
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-attr = 'user_id'
-
- def __getattr__(self, attr):
- """Generate methods for each HTTP request type (GET, POST, etc.)"""
- methods = ["get", "post", "put", "delete", "patch", "head", "options"]
- if attr in methods:
- return partial(self.request, attr)
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'SquareletClient' object has no attribute 'user_id'
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:180: AttributeError
-_____________ TestProjectClient.test_get_or_create_by_title_create _____________
-
-self =
-client =
-
- def test_get_or_create_by_title_create(self, client):
- title = "Created Title"
-> project, created = client.projects.get_or_create_by_title(title)
-
-tests/test_projects.py:108:
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-documentcloud/projects.py:146: in get_or_create_by_title
- project = self.get(title=title)
-documentcloud/projects.py:115: in get
- return self.get_by_title(title)
-documentcloud/projects.py:122: in get_by_title
- f"{self.api_path}/", params={"title": title, "user": self.client.user_id}
-_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
-
-self =
-attr = 'user_id'
-
- def __getattr__(self, attr):
- """Generate methods for each HTTP request type (GET, POST, etc.)"""
- methods = ["get", "post", "put", "delete", "patch", "head", "options"]
- if attr in methods:
- return partial(self.request, attr)
-> raise AttributeError(
- f"'{self.__class__.__name__}' object has no attribute '{attr}'"
- )
-E AttributeError: 'SquareletClient' object has no attribute 'user_id'
-
-../../.local/lib/python3.11/site-packages/squarelet/squarelet.py:180: AttributeError
-=========================== short test summary info ============================
-FAILED tests/test_client.py::test_set_tokens_credentials - AttributeError: 'D...
-FAILED tests/test_client.py::test_set_tokens_refresh - AttributeError: 'Docum...
-FAILED tests/test_client.py::test_get_tokens - AttributeError: 'DocumentCloud...
-FAILED tests/test_client.py::test_get_tokens_bad_credentials - AttributeError...
-FAILED tests/test_client.py::test_refresh_tokens - AttributeError: 'DocumentC...
-FAILED tests/test_documents.py::TestDocument::test_user - AttributeError: 'Do...
-FAILED tests/test_documents.py::TestDocument::test_organization - AttributeEr...
-FAILED tests/test_documents.py::TestDocument::test_attrs[contributor] - Attri...
-FAILED tests/test_documents.py::TestDocument::test_attrs[contributor_organization]
-FAILED tests/test_documents.py::TestDocument::test_attrs[contributor_organization_slug]
-FAILED tests/test_documents.py::TestDocumentClient::test_list - assert 8 > 8
-FAILED tests/test_projects.py::TestProjectClient::test_list - AttributeError:...
-FAILED tests/test_projects.py::TestProjectClient::test_get_title - AttributeE...
-FAILED tests/test_projects.py::TestProjectClient::test_get_by_title - Attribu...
-FAILED tests/test_projects.py::TestProjectClient::test_get_by_title_multiple
-FAILED tests/test_projects.py::TestProjectClient::test_get_or_create_by_title_get
-FAILED tests/test_projects.py::TestProjectClient::test_get_or_create_by_title_create
-ERROR tests/test_client.py::test_set_tokens_none - ValueError: No tokens found
-ERROR tests/test_client.py::test_user_id_public - ValueError: No tokens found
-ERROR tests/test_documents.py::TestDocumentClient::test_public_upload - Value...
-ERROR tests/test_documents.py::TestDocumentClient::test_upload_big_file
-====== 17 failed, 117 passed, 2 deselected, 4 errors in 122.59s (0:02:02) ======
From bebddc5c6ff5e4d13f087f962b531841ecb8ea0e Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:03:29 -0600
Subject: [PATCH 2/7] Remove travis
---
.travis.yml | 24 ------------------------
1 file changed, 24 deletions(-)
delete mode 100644 .travis.yml
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 98bf2ad..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,24 +0,0 @@
-language: python
-
-sudo: false
-
-python:
- - '2.7'
- - '3.7-dev'
-
-install:
- - pip install -r requirements.txt
-
-script:
- - flake8 documentcloud
- - coverage run setup.py test
-
-after_success:
- - coveralls
-
-env:
- global:
- # Encrypted DOCUMENTCLOUD_TEST_USERNAME
- - secure: "mt7PbdPrHREewNkZE+hV/HILK+67IYVl2F/SZi57VZYx7zBX2+jwZbAAbiWwGrOG9zqgqGiovH+Bx1NAPDm6V0vdlEaXzxhqpKxd4KKWCocL5tZP+6mXbN36Cifm04TIvFUBG0auCR2OMoewxgaY0a/vr6+KYYj37uAlUPhfBcA="
- # Encrypted DOCUMENTCLOUD_TEST_PASSWORD
- - secure: "ZoGrJ9lPyDPDRwMcL5Wx+t+BNPnwpuRy2d4c6z/lTYhdyGwHycoQJCMcvgMkKm/VQP76iLR2efuwV4GUGPRD4YkIBqEgYrLtEvxHUAoLQoo3+uylPrmfZDQtLTmXN5ZKR+dSGFLs9mMQhU0zARpdgSIMFlz2xgFKMpjvzN3lHmc="
From 1cfea3a85c26caff9251e3e571ebbeaacf58da60 Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:09:51 -0600
Subject: [PATCH 3/7] Remove references to LA Times desk
---
docs/conf.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/docs/conf.py b/docs/conf.py
index 6a55a2d..2f4aa7b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -206,7 +206,7 @@
"index",
"python-documentcloud.tex",
"python-documentcloud Documentation",
- "Los Angeles Times Data Desk",
+ "MuckRock Foundation",
"manual",
),
]
@@ -241,7 +241,7 @@
"index",
"python-documentcloud",
"python-documentcloud Documentation",
- ["Los Angeles Times Data Desk"],
+ ["MuckRock Foundation"],
1,
)
]
@@ -260,7 +260,7 @@
"index",
"python-documentcloud",
"python-documentcloud Documentation",
- "Los Angeles Times Data Desk",
+ "MuckRock Foundation",
"python-documentcloud",
"One line description of project.",
"Miscellaneous",
From 2cc618b1881cbf8df1ec28f35c2e6e4f0ba7a221 Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:16:35 -0600
Subject: [PATCH 4/7] Removes testing for 3.7
---
.github/workflows/main.yml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index a2facc2..13e0a02 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
- python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
steps:
- name: Check out code
uses: actions/checkout@v2
From 9c8f91de0a5c7e476803e2d0fb73f44bff201cda Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:39:23 -0600
Subject: [PATCH 5/7] Fix pylint
---
.pylintrc | 3 +++
documentcloud/client.py | 6 ------
2 files changed, 3 insertions(+), 6 deletions(-)
diff --git a/.pylintrc b/.pylintrc
index 3276277..d75dfc4 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -4,3 +4,6 @@ good-names=i,x1,x2,y1,y2,id
[MESSAGES CONTROL]
disable=missing-docstring,too-many-ancestors,too-few-public-methods,no-else-return,no-member,attribute-defined-outside-init,similarities,import-outside-toplevel,cyclic-import,no-member,no-else-raise,too-many-instance-attributes,too-many-arguments,ungrouped-imports,useless-object-inheritance,no-else-continue
+
+[DESIGN]
+max-positional-arguments=10
\ No newline at end of file
diff --git a/documentcloud/client.py b/documentcloud/client.py
index dc371b9..61b14a5 100644
--- a/documentcloud/client.py
+++ b/documentcloud/client.py
@@ -18,7 +18,6 @@ class DocumentCloud(SquareletClient):
"""
The public interface for the DocumentCloud API, now integrated with SquareletClient
"""
- # pylint:disable=too-many-positional-arguments
def __init__(
self,
username=None,
@@ -55,8 +54,3 @@ def __init__(
self.projects = ProjectClient(self)
self.users = UserClient(self)
self.organizations = OrganizationClient(self)
-
- """def _request(self, method, url, raise_error=True, **kwargs):
- Delegates request to the SquareletClient's _request method
- return self.squarelet_client.request(method, url, raise_error, **kwargs)
- """
\ No newline at end of file
From 6c4aa796ae3792a16acb3d47c56bdddb4916d09e Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:43:32 -0600
Subject: [PATCH 6/7] Fixed workflow file to use --check for black
---
.github/workflows/main.yml | 2 +-
documentcloud/client.py | 6 ++++--
documentcloud/documents.py | 18 ++++++------------
tests/test_documents.py | 5 +----
4 files changed, 12 insertions(+), 19 deletions(-)
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 13e0a02..19d062f 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -53,4 +53,4 @@ jobs:
- name: Run pylint and black on ./documentcloud and ./tests
run: |
- pylint ./documentcloud ./tests; black ./documentcloud ./tests
+ pylint ./documentcloud ./tests; black --check ./documentcloud ./tests
diff --git a/documentcloud/client.py b/documentcloud/client.py
index 61b14a5..1acee53 100644
--- a/documentcloud/client.py
+++ b/documentcloud/client.py
@@ -14,10 +14,12 @@
logger = logging.getLogger("documentcloud")
+
class DocumentCloud(SquareletClient):
"""
The public interface for the DocumentCloud API, now integrated with SquareletClient
"""
+
def __init__(
self,
username=None,
@@ -29,7 +31,7 @@ def __init__(
rate_limit=True,
rate_limit_sleep=True,
):
- # Initialize SquareletClient for authentication and request handling
+ # Initialize SquareletClient for authentication and request handling
super().__init__(
base_uri=base_uri,
username=username,
@@ -37,7 +39,7 @@ def __init__(
auth_uri=auth_uri,
timeout=timeout,
rate_limit=rate_limit,
- rate_limit_sleep=rate_limit_sleep
+ rate_limit_sleep=rate_limit_sleep,
)
# Set up logging
diff --git a/documentcloud/documents.py b/documentcloud/documents.py
index 0d417a1..7ea2c59 100644
--- a/documentcloud/documents.py
+++ b/documentcloud/documents.py
@@ -404,9 +404,7 @@ def upload_directory(self, path, handle_errors=False, extensions=".pdf", **kwarg
path_list = self._collect_files(path, extensions)
logger.info(
- "Upload directory on %s: Found %d files to upload",
- path,
- len(path_list)
+ "Upload directory on %s: Found %d files to upload", path, len(path_list)
)
# Upload all the files using the bulk API to reduce the number
@@ -444,7 +442,7 @@ def upload_directory(self, path, handle_errors=False, extensions=".pdf", **kwarg
logger.info(
"Error creating the following documents: %s\n%s",
exc,
- "\n".join(file_paths)
+ "\n".join(file_paths),
)
continue
else:
@@ -465,7 +463,7 @@ def upload_directory(self, path, handle_errors=False, extensions=".pdf", **kwarg
logger.info(
"Error uploading the following document: %s %s",
exc,
- file_path
+ file_path,
)
continue
else:
@@ -481,7 +479,7 @@ def upload_directory(self, path, handle_errors=False, extensions=".pdf", **kwarg
logger.info(
"Error creating the following documents: %s\n%s",
exc,
- "\n".join(file_paths)
+ "\n".join(file_paths),
)
continue
else:
@@ -504,11 +502,7 @@ def upload_urls(self, url_list, handle_errors=False, **kwargs):
# Grouper will put None's on the end of the last group
url_group = [url for url in url_group if url is not None]
- logger.info(
- "Uploading group %d: %s",
- i + 1,
- "\n".join(url_group)
- )
+ logger.info("Uploading group %d: %s", i + 1, "\n".join(url_group))
# Create the documents
logger.info("Creating the documents...")
@@ -531,7 +525,7 @@ def upload_urls(self, url_list, handle_errors=False, **kwargs):
logger.info(
"Error creating the following documents: %s\n%s",
str(exc),
- "\n".join(url_group)
+ "\n".join(url_group),
)
continue
else:
diff --git a/tests/test_documents.py b/tests/test_documents.py
index 990d97a..cbe0017 100644
--- a/tests/test_documents.py
+++ b/tests/test_documents.py
@@ -158,9 +158,7 @@ def test_section(self, document_factory):
class TestDocumentClient:
def test_search(self, client, document):
- documents = client.documents.search(
- f"document:{document.id} simple"
- )
+ documents = client.documents.search(f"document:{document.id} simple")
assert documents
def test_list(self, client):
@@ -182,7 +180,6 @@ def test_upload_file(self, document_factory):
document = document_factory(pdf)
assert document.status == "success"
-
def test_upload_file_path(self, document_factory):
document = document_factory("tests/test.pdf")
assert document.status == "success"
From a43f1bad76892843d5028f42a83fea345e6c9454 Mon Sep 17 00:00:00 2001
From: duckduckgrayduck <102841251+duckduckgrayduck@users.noreply.github.com>
Date: Wed, 19 Feb 2025 10:47:36 -0600
Subject: [PATCH 7/7] Remove support for python 3.7
---
docs/changelog.rst | 4 ++++
setup.py | 3 +--
2 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/docs/changelog.rst b/docs/changelog.rst
index 11ccc4b..f98dca2 100644
--- a/docs/changelog.rst
+++ b/docs/changelog.rst
@@ -1,6 +1,10 @@
Changelog
---------
+4.4.0
+~~~~~
+* Removes support for Python 3.7.
+
4.3.0
~~~~~
* Uses python-squarelet to client handle authentication.
diff --git a/setup.py b/setup.py
index 4cdb393..6d78736 100644
--- a/setup.py
+++ b/setup.py
@@ -7,7 +7,7 @@
setup(
name="python-documentcloud",
- version="4.3.0",
+ version="4.4.0",
description="A simple Python wrapper for the DocumentCloud API",
author="Mitchell Kotler",
author_email="mitch@muckrock.com",
@@ -49,7 +49,6 @@
"Intended Audience :: Developers",
"Operating System :: OS Independent",
"License :: OSI Approved :: MIT License",
- "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",