From 5ff649ec7f4ad9074473626fb553609b18c560f6 Mon Sep 17 00:00:00 2001 From: mcarans Date: Fri, 8 May 2026 11:57:34 +1200 Subject: [PATCH 1/4] Add the actions --- src/hdx/data/resource.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hdx/data/resource.py b/src/hdx/data/resource.py index 437dc77..1b11b95 100755 --- a/src/hdx/data/resource.py +++ b/src/hdx/data/resource.py @@ -62,6 +62,9 @@ def actions() -> dict[str, str]: "search": "resource_search", "broken": "hdx_mark_broken_link_in_resource", "datastore_delete": "datastore_delete", + "datastore_create": "datastore_create", + "datastore_insert": "datastore_insert", + "datastore_upsert": "datastore_upsert", "datastore_search": "datastore_search", } From 375fe2592ba367e4fef723e235d315d398a3a4d8 Mon Sep 17 00:00:00 2001 From: mcarans Date: Mon, 18 May 2026 08:45:46 +1200 Subject: [PATCH 2/4] Add new methods --- CLAUDE.md | 57 +++++++++++++++++++++++++++++++++ pyproject.toml | 1 + src/hdx/data/resource.py | 47 +++++++++++++++++++++++++++ tests/hdx/data/test_resource.py | 33 ++++++++++++++++++- uv.lock | 2 ++ 5 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..9f9e095 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,57 @@ +# CLAUDE.md + +## Project Overview + +**hdx-python-api** is the official Python library for interacting with the [Humanitarian Data Exchange (HDX)](https://data.humdata.org/) platform. It provides ORM-like classes wrapping the CKAN API to read, create, update, and delete datasets, resources, organizations, users, vocabularies, showcases, and datastores. + +## Source Layout + +- `src/hdx/api/` — Configuration, session management, locations, utilities + - `configuration.py` — `Configuration` class (credentials, site URL, user agent) + - `remotehdx.py` — Low-level CKAN API client +- `src/hdx/data/` — Data model objects, all inheriting from `HDXObject` + - `dataset.py`, `resource.py`, `organization.py`, `user.py`, `vocabulary.py`, `showcase.py`, `resource_view.py` + - `hdxobject.py` — Base class providing `_read_from_hdx` / `_write_to_hdx` and CRUD scaffolding +- `src/hdx/facades/` — High-level entry-point helpers (`simple`, `keyword_arguments`, `infer_arguments`) +- `tests/hdx/` — Test suite mirroring src layout; fixtures in `tests/fixtures/` + +## Running Tests + +```bash +uv run pytest +``` + +Tests mock the CKAN HTTP session via inner `MockSession` classes in each test file — no live HDX connection needed for the standard suite. Integration tests require `HDX_KEY_TEST` and `HDX_PIPELINE_GSHEET_AUTH` environment variables. + +Coverage is written to `coverage.lcov` and JUnit XML to `test-results.xml`. + +## Code Style + +Formatted and linted with `ruff` (rules: E, F, I, UP; line-length not enforced). Run before committing: + +```bash +pre-commit run --all-files +``` + +- Python ≥ 3.10 +- Type hints throughout; use `X | Y` union syntax (PEP 604), not `Optional`/`Union` +- Google-style docstrings with `Args:` and `Returns:` sections +- No inline comments unless the *why* is non-obvious + +## Key Patterns + +**HDXObject subclasses** expose a standard interface: `create_in_hdx()`, `update_in_hdx()`, `delete_from_hdx()`, `read_from_hdx()`. Each class defines an `actions()` static method mapping action names to CKAN API action strings. + +**Writing to HDX** uses `self._write_to_hdx(action_key, data_dict, id_field_name)` inherited from `HDXObject`. The `action_key` must be in `actions()`. + +**Test mocking** — each test fixture defines a `MockSession` with a `post()` method that inspects the URL and decoded JSON body to return `MockResponse` objects. State is tracked on class variables (e.g. `TestResource.datastore`). + +## Collaboration Style + +- Be objective, not agreeable. Act as a partner, not a sycophant. Push back when you disagree, flag tradeoffs honestly, and don't sugarcoat problems. +- Keep explanations brief and to the point. +- Don't rely on recalled knowledge for facts that could be stale (API behaviour, library versions, external systems). Search or read the actual source first. If you lack verified information, say so rather than speculate. + +## Scope of Changes + +When fixing a bug or addressing PR feedback, change only what is necessary to resolve the specific issue. Do not refactor surrounding code, rename variables, adjust formatting, or make improvements in the same commit unless they are directly required by the fix. diff --git a/pyproject.toml b/pyproject.toml index 6b68553..9d465e0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -37,6 +37,7 @@ requires-python = ">=3.10" dependencies = [ "ckanapi>=4.8", + "coverage>=7.13.2", "defopt>=7.0.0", "email_validator", "hdx-python-country>=4.1.1", diff --git a/src/hdx/data/resource.py b/src/hdx/data/resource.py index 1b11b95..531db49 100755 --- a/src/hdx/data/resource.py +++ b/src/hdx/data/resource.py @@ -704,6 +704,53 @@ def has_datastore(self) -> bool: return True return False + def create_datastore( + self, + schema: list[dict], + primary_key: str | list[str] | None = None, + ) -> None: + """Create a datastore for the resource with the given schema. + + Args: + schema: List of field definitions, each a dict with 'id' and 'type' keys. + primary_key: Primary key field name(s). Defaults to None. + + Returns: + None + """ + data: dict = { + "resource_id": self.data["id"], + "force": True, + "fields": schema, + } + if primary_key is not None: + if isinstance(primary_key, list): + primary_key = ",".join(primary_key) + data["primary_key"] = primary_key + self._write_to_hdx("datastore_create", data, "resource_id") + + def update_datastore( + self, + records: list[dict], + method: str = "upsert", + ) -> None: + """Update (upsert) records into the resource datastore. + + Args: + records: List of record dicts to insert or update. + method: Datastore update method ('upsert', 'insert', or 'update'). Defaults to 'upsert'. + + Returns: + None + """ + data = { + "resource_id": self.data["id"], + "force": True, + "method": method, + "records": records, + } + self._write_to_hdx("datastore_upsert", data, "resource_id") + def delete_datastore(self) -> None: """Delete a resource from the HDX datastore diff --git a/tests/hdx/data/test_resource.py b/tests/hdx/data/test_resource.py index 1c4f488..001476b 100755 --- a/tests/hdx/data/test_resource.py +++ b/tests/hdx/data/test_resource.py @@ -573,10 +573,18 @@ def post(url, data, headers, files, allow_redirects, auth=None): 200, '{"success": true, "result": {"include_total": true, "resource_id": "_table_metadata", "fields": [{"type": "int", "id": "_id"}, {"type": "name", "id": "name"}, {"type": "oid", "id": "oid"}, {"type": "name", "id": "alias_of"}], "records_format": "objects", "records": [{"_id":"f9cd60f3d7f2f6d0","name":"f9228459-d808-4b51-948f-68a5850abfde","oid":"919290","alias_of":null},{"_id":"7ae63490de9b7d7b","name":"af618a0b-09b8-42c8-836f-2be597e1ea34","oid":"135294","alias_of":null},{"_id":"1dc37f4e89988644","name":"748b40dd-7bd3-40a3-941b-e76f0bfbe0eb","oid":"117144","alias_of":null},{"_id":"2a554a61bd366206","name":"91c78d24-eab3-40b5-ba91-6b29bcda7178","oid":"116963","alias_of":null},{"_id":"fd787575143afe90","name":"9320cfce-4620-489a-bcbe-25c73867d4fc","oid":"107430","alias_of":null},{"_id":"a70093abd230f647","name":"b9d2eb36-e65c-417a-bc28-f4dadb149302","oid":"107409","alias_of":null},{"_id":"95fbdd2d06c07aea","name":"ca6a0891-8395-4d58-9168-6c44e17e0193","oid":"107385","alias_of":null}], "limit": 10000, "_links": {"start": "/api/action/datastore_search?limit=10000&resource_id=_table_metadata", "next": "/api/action/datastore_search?offset=10000&limit=10000&resource_id=_table_metadata"}, "total": 7}}', ) + if ( + "upsert" in url + and datadict["resource_id"] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5" + ): + TestResource.datastore = "upsert" + return MockResponse( + 200, + '{"success": true, "result": {"method": "upsert", "resource_id": "de6549d8-268b-4dfe-adaf-a4ae5c8510d5"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=datastore_upsert"}', + ) if ( "create" in url or "insert" in url - or "upsert" in url or "search" in url ) and datadict["resource_id"] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5": TestResource.datastore = "create" @@ -977,6 +985,29 @@ def test_datastore(self, configuration, post_datastore, topline_yaml, topline_js TestResource.datastore = None assert resource2.has_datastore() is False + schema = [{"id": "code", "type": "text"}, {"id": "value", "type": "float"}] + records = [{"code": "A", "value": 1.0}, {"code": "B", "value": 2.0}] + + TestResource.datastore = None + resource.create_datastore(schema, primary_key="code") + assert TestResource.datastore == "create" + + TestResource.datastore = None + resource.create_datastore(schema, primary_key=["code", "value"]) + assert TestResource.datastore == "create" + + TestResource.datastore = None + resource.create_datastore(schema) + assert TestResource.datastore == "create" + + TestResource.datastore = None + resource.update_datastore(records) + assert TestResource.datastore == "upsert" + + TestResource.datastore = None + resource.update_datastore(records, method="insert") + assert TestResource.datastore == "upsert" + def test_resource_views(self, configuration, post_resourceview): resource = Resource({"id": "25982d1c-f45a-45e1-b14e-87d367413045"}) with pytest.raises(HDXError): diff --git a/uv.lock b/uv.lock index 8b767c6..67dd7df 100644 --- a/uv.lock +++ b/uv.lock @@ -486,6 +486,7 @@ name = "hdx-python-api" source = { editable = "." } dependencies = [ { name = "ckanapi" }, + { name = "coverage" }, { name = "defopt" }, { name = "email-validator" }, { name = "hdx-python-country" }, @@ -513,6 +514,7 @@ dev = [ [package.metadata] requires-dist = [ { name = "ckanapi", specifier = ">=4.8" }, + { name = "coverage", specifier = ">=7.13.2" }, { name = "defopt", specifier = ">=7.0.0" }, { name = "email-validator" }, { name = "hdx-python-country", specifier = ">=4.1.1" }, From 476ca901119fd541495b3e7c8ea17e234cbb32a8 Mon Sep 17 00:00:00 2001 From: mcarans Date: Mon, 18 May 2026 10:13:39 +1200 Subject: [PATCH 3/4] Add new methods --- .pre-commit-config.yaml | 2 +- pyproject.toml | 3 +-- tests/hdx/data/test_resource.py | 11 +++++------ uv.lock | 11 ++++------- 4 files changed, 11 insertions(+), 16 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index e1e60aa..47fa276 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ default_language_version: - python: python3.13 + python: python3 repos: - repo: https://github.com/pre-commit/pre-commit-hooks diff --git a/pyproject.toml b/pyproject.toml index 9d465e0..464d207 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,8 +36,7 @@ dynamic = ["version"] requires-python = ">=3.10" dependencies = [ - "ckanapi>=4.8", - "coverage>=7.13.2", + "ckanapi>=4.11", "defopt>=7.0.0", "email_validator", "hdx-python-country>=4.1.1", diff --git a/tests/hdx/data/test_resource.py b/tests/hdx/data/test_resource.py index 001476b..64d18a1 100755 --- a/tests/hdx/data/test_resource.py +++ b/tests/hdx/data/test_resource.py @@ -575,18 +575,17 @@ def post(url, data, headers, files, allow_redirects, auth=None): ) if ( "upsert" in url - and datadict["resource_id"] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5" + and datadict["resource_id"] + == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5" ): TestResource.datastore = "upsert" return MockResponse( 200, '{"success": true, "result": {"method": "upsert", "resource_id": "de6549d8-268b-4dfe-adaf-a4ae5c8510d5"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=datastore_upsert"}', ) - if ( - "create" in url - or "insert" in url - or "search" in url - ) and datadict["resource_id"] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5": + if ("create" in url or "insert" in url or "search" in url) and datadict[ + "resource_id" + ] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5": TestResource.datastore = "create" return MockResponse( 200, diff --git a/uv.lock b/uv.lock index 67dd7df..3a81534 100644 --- a/uv.lock +++ b/uv.lock @@ -170,7 +170,7 @@ wheels = [ [[package]] name = "ckanapi" -version = "4.9" +version = "4.11" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "docopt" }, @@ -178,11 +178,10 @@ dependencies = [ { name = "requests" }, { name = "setuptools" }, { name = "simplejson" }, - { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/52/4b/c8e6ad9c549918c9503b59c7ce95fd49c211d862c82229df2034ec661c41/ckanapi-4.9.tar.gz", hash = "sha256:4d5d59da939586e9f34324c016f5cbea1f6ab4753f792d45d24379aa79ccbc06", size = 37717, upload-time = "2025-11-10T19:50:49.35Z" } +sdist = { url = "https://files.pythonhosted.org/packages/be/fd/53ad4c91cba8eb829262f723d38f63da07778e056f8dbe17547a90bf8591/ckanapi-4.11.tar.gz", hash = "sha256:913dc04e23e16dac9357d013eefda797f2ae5b7a5b584077c10b70d7336b467e", size = 43151, upload-time = "2026-03-20T14:51:19.806Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/14/83/ee5d03e4394a1bcb69215321ac6f116fb02554cee40b0390b9190cf052d5/ckanapi-4.9-py3-none-any.whl", hash = "sha256:845c066dd7bdfc768644aee50336058aafddc70b1db23649a48e05782032bf45", size = 46527, upload-time = "2025-11-10T19:50:47.123Z" }, + { url = "https://files.pythonhosted.org/packages/de/c9/c8e1c0e0188a60fc2dc77b7d5dec784ea7152edc0f3a79e162a08786ce5c/ckanapi-4.11-py3-none-any.whl", hash = "sha256:6625efd482ef969487dfcd3cc5d6c81d296cf240c7371b7daf5e7a5e430a9f2a", size = 50959, upload-time = "2026-03-20T14:51:18.208Z" }, ] [[package]] @@ -486,7 +485,6 @@ name = "hdx-python-api" source = { editable = "." } dependencies = [ { name = "ckanapi" }, - { name = "coverage" }, { name = "defopt" }, { name = "email-validator" }, { name = "hdx-python-country" }, @@ -513,8 +511,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "ckanapi", specifier = ">=4.8" }, - { name = "coverage", specifier = ">=7.13.2" }, + { name = "ckanapi", specifier = ">=4.11" }, { name = "defopt", specifier = ">=7.0.0" }, { name = "email-validator" }, { name = "hdx-python-country", specifier = ">=4.1.1" }, From 3d9a9830f075f46a2bda9e4d7b168a4ccb44d780 Mon Sep 17 00:00:00 2001 From: mcarans Date: Mon, 18 May 2026 10:44:09 +1200 Subject: [PATCH 4/4] Fix tests broken by new ckanapi --- src/hdx/data/resource.py | 12 +++++----- tests/hdx/api/utilities/test_hdx_state.py | 4 ++-- tests/hdx/data/test_dataset_add_hapi_error.py | 2 +- tests/hdx/data/test_dataset_core.py | 20 ++++++++--------- tests/hdx/data/test_dataset_noncore.py | 10 ++++----- tests/hdx/data/test_organization.py | 18 +++++++-------- tests/hdx/data/test_resource.py | 22 ++++++++++--------- tests/hdx/data/test_resource_view.py | 10 ++++----- tests/hdx/data/test_showcase.py | 10 ++++----- tests/hdx/data/test_user.py | 22 +++++++++---------- tests/hdx/data/test_vocabulary.py | 12 +++++----- 11 files changed, 72 insertions(+), 70 deletions(-) diff --git a/src/hdx/data/resource.py b/src/hdx/data/resource.py index 531db49..9277de3 100755 --- a/src/hdx/data/resource.py +++ b/src/hdx/data/resource.py @@ -706,13 +706,13 @@ def has_datastore(self) -> bool: def create_datastore( self, - schema: list[dict], - primary_key: str | list[str] | None = None, + schema: Sequence[dict], + primary_key: str | Sequence[str] | None = None, ) -> None: """Create a datastore for the resource with the given schema. Args: - schema: List of field definitions, each a dict with 'id' and 'type' keys. + schema: Sequence of field definitions, each a dict with 'id' and 'type' keys. primary_key: Primary key field name(s). Defaults to None. Returns: @@ -724,20 +724,20 @@ def create_datastore( "fields": schema, } if primary_key is not None: - if isinstance(primary_key, list): + if not isinstance(primary_key, str): primary_key = ",".join(primary_key) data["primary_key"] = primary_key self._write_to_hdx("datastore_create", data, "resource_id") def update_datastore( self, - records: list[dict], + records: Sequence[dict], method: str = "upsert", ) -> None: """Update (upsert) records into the resource datastore. Args: - records: List of record dicts to insert or update. + records: Sequence of record dicts to insert or update. method: Datastore update method ('upsert', 'insert', or 'update'). Defaults to 'upsert'. Returns: diff --git a/tests/hdx/api/utilities/test_hdx_state.py b/tests/hdx/api/utilities/test_hdx_state.py index ae90c93..e57f42c 100644 --- a/tests/hdx/api/utilities/test_hdx_state.py +++ b/tests/hdx/api/utilities/test_hdx_state.py @@ -47,7 +47,7 @@ def date2(self): def do_state(self, tempfolder, statefile): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if "resource" in url: result = json.dumps(resultdict) return MockResponse( @@ -71,7 +71,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def do_state_multi(self, tempfolder, multidatestatefile): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if "resource" in url: result = json.dumps(resultdict) return MockResponse( diff --git a/tests/hdx/data/test_dataset_add_hapi_error.py b/tests/hdx/data/test_dataset_add_hapi_error.py index 48e99a4..bc27aea 100644 --- a/tests/hdx/data/test_dataset_add_hapi_error.py +++ b/tests/hdx/data/test_dataset_add_hapi_error.py @@ -13,7 +13,7 @@ class TestDatasetAddHAPIError: def hapi_resource_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: resource_id = datadict["id"] diff --git a/tests/hdx/data/test_dataset_core.py b/tests/hdx/data/test_dataset_core.py index c26d163..c814865 100755 --- a/tests/hdx/data/test_dataset_core.py +++ b/tests/hdx/data/test_dataset_core.py @@ -182,7 +182,7 @@ def static_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return dataset_mockshow(url, datadict) @@ -192,7 +192,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_revise(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() @@ -219,7 +219,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() @@ -286,7 +286,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() @@ -363,7 +363,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_reorder(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -390,7 +390,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -423,7 +423,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def search(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mocksearch(url, datadict) @@ -433,7 +433,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_list(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mocklist(url, datadict) @@ -443,7 +443,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def all(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mockall(url, datadict) @@ -453,7 +453,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_autocomplete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "autocomplete" not in url or "acled" not in datadict["q"]: diff --git a/tests/hdx/data/test_dataset_noncore.py b/tests/hdx/data/test_dataset_noncore.py index 6b1138d..726d9e8 100755 --- a/tests/hdx/data/test_dataset_noncore.py +++ b/tests/hdx/data/test_dataset_noncore.py @@ -52,7 +52,7 @@ def vocabulary_read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return vocabulary_mockshow(url, datadict) @@ -62,7 +62,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def user_read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return user_mockshow(url, datadict) @@ -72,7 +72,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def organization_read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return organization_mockshow(url, datadict) @@ -82,7 +82,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def showcase_read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "showcase_list" in url: result = json.dumps([showcase_resultdict]) @@ -111,7 +111,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def vocabulary_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() diff --git a/tests/hdx/data/test_organization.py b/tests/hdx/data/test_organization.py index 8e66c4e..2087ddb 100755 --- a/tests/hdx/data/test_organization.py +++ b/tests/hdx/data/test_organization.py @@ -115,7 +115,7 @@ def static_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return organization_mockshow(url, datadict) @@ -125,7 +125,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return organization_mockshow(url, datadict) @@ -165,7 +165,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return organization_mockshow(url, datadict) @@ -205,7 +205,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -232,7 +232,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_list(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): json.loads(data.decode("utf-8")) return mocklist(url) @@ -242,7 +242,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_all_fields(self, fixturesfolder): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): kwargs = json.loads(data.decode("utf-8")) if "show" in url: return organization_mockshow(url, kwargs) @@ -264,7 +264,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def user_read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return user_mockshow(url, datadict) @@ -274,7 +274,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def datasets_get(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mockgetdatasets(url, datadict) @@ -284,7 +284,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_autocomplete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "autocomplete" not in url or "innago" not in datadict["q"]: diff --git a/tests/hdx/data/test_resource.py b/tests/hdx/data/test_resource.py index 64d18a1..dcc4281 100755 --- a/tests/hdx/data/test_resource.py +++ b/tests/hdx/data/test_resource.py @@ -359,7 +359,7 @@ def topline_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mockshow(url, datadict) @@ -369,7 +369,8 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): + files = kwargs.get("files") if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() @@ -431,7 +432,8 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): + files = kwargs.get("files") if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() @@ -491,7 +493,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -518,7 +520,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_datastore(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -602,7 +604,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_patch(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return mockshow(url, datadict) @@ -614,7 +616,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_dataset(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mockdataset(url, datadict) @@ -624,7 +626,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def search(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mocksearch(url, datadict) @@ -634,7 +636,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_resourceview(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") return mockresourceview(url, decodedata) @@ -644,7 +646,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_broken(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if isinstance(data, dict): datadict = { k.decode("utf8"): v.decode("utf8") for k, v in data.items() diff --git a/tests/hdx/data/test_resource_view.py b/tests/hdx/data/test_resource_view.py index 5e9a110..a473c95 100755 --- a/tests/hdx/data/test_resource_view.py +++ b/tests/hdx/data/test_resource_view.py @@ -136,7 +136,7 @@ def static_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return resource_view_mockshow(url, datadict) @@ -146,7 +146,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return resource_view_mockshow(url, datadict) @@ -165,7 +165,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return resource_view_mockshow(url, datadict) @@ -207,7 +207,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -234,7 +234,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_list(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return resource_view_mocklist(url, datadict) diff --git a/tests/hdx/data/test_showcase.py b/tests/hdx/data/test_showcase.py index 01ec043..b675bc8 100755 --- a/tests/hdx/data/test_showcase.py +++ b/tests/hdx/data/test_showcase.py @@ -185,7 +185,7 @@ def static_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "association_delete" in url: TestShowcase.association = "delete" @@ -208,7 +208,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "vocabulary" in url: return vocabulary_mockshow(url, datadict) @@ -253,7 +253,7 @@ def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "vocabulary" in url: return vocabulary_mockshow(url, datadict) @@ -295,7 +295,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if url.endswith("show") or "list" in url: @@ -322,7 +322,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def allsearch(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return mockallsearch(url, datadict) diff --git a/tests/hdx/data/test_user.py b/tests/hdx/data/test_user.py index 3169749..3b09a8a 100755 --- a/tests/hdx/data/test_user.py +++ b/tests/hdx/data/test_user.py @@ -147,7 +147,7 @@ def static_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return user_mockshow(url, datadict) @@ -157,7 +157,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return user_mockshow(url, datadict) @@ -197,7 +197,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return user_mockshow(url, datadict) @@ -237,7 +237,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "show" in url: @@ -264,7 +264,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def show_current_user(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): if "show" not in url: return MockResponse( 404, @@ -282,7 +282,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_list(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): json.loads(data.decode("utf-8")) return mocklist(url) @@ -292,7 +292,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_listorgs(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "user" in url: @@ -321,7 +321,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_check_current_user_write_access(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "user" in url: @@ -361,7 +361,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_listorgs_invalid(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "user" in url: @@ -379,7 +379,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_tokenlist(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "user" in url: @@ -402,7 +402,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_autocomplete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "autocomplete" not in url or "fake" not in datadict["q"]: diff --git a/tests/hdx/data/test_vocabulary.py b/tests/hdx/data/test_vocabulary.py index 1f0eca4..48ac0e4 100755 --- a/tests/hdx/data/test_vocabulary.py +++ b/tests/hdx/data/test_vocabulary.py @@ -1137,7 +1137,7 @@ def static_json(self, configfolder): def read(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return vocabulary_mockshow(url, datadict) @@ -1147,7 +1147,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_create(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return vocabulary_mockshow(url, datadict) @@ -1193,7 +1193,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_update(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) if "show" in url: return vocabulary_mockshow(url, datadict) @@ -1245,7 +1245,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_delete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return vocabulary_delete(url, datadict) @@ -1255,7 +1255,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_list(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): datadict = json.loads(data.decode("utf-8")) return vocabulary_mocklist(url, datadict) @@ -1265,7 +1265,7 @@ def post(url, data, headers, files, allow_redirects, auth=None): def post_autocomplete(self): class MockSession: @staticmethod - def post(url, data, headers, files, allow_redirects, auth=None): + def post(url, data, **kwargs): decodedata = data.decode("utf-8") datadict = json.loads(decodedata) if "autocomplete" not in url or "health" not in datadict["q"]: