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",