hdx-python-api 6.6.7__tar.gz → 6.6.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.pre-commit-config.yaml +1 -1
  2. hdx_python_api-6.6.8/CLAUDE.md +57 -0
  3. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/PKG-INFO +2 -2
  4. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/pyproject.toml +1 -1
  5. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/_version.py +2 -2
  6. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/resource.py +50 -0
  7. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_hdx_state.py +2 -2
  8. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_add_hapi_error.py +1 -1
  9. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_core.py +10 -10
  10. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_noncore.py +5 -5
  11. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_organization.py +9 -9
  12. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_resource.py +47 -15
  13. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_resource_view.py +5 -5
  14. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_showcase.py +5 -5
  15. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_user.py +11 -11
  16. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_vocabulary.py +6 -6
  17. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/uv.lock +4 -5
  18. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.github/workflows/publish.yaml +0 -0
  19. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.github/workflows/run-python-tests.yaml +0 -0
  20. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/.gitignore +0 -0
  21. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/LICENSE +0 -0
  22. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/README.md +0 -0
  23. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/documentation/.readthedocs.yaml +0 -0
  24. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/documentation/index.md +0 -0
  25. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/documentation/mkdocs.yaml +0 -0
  26. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/__init__.py +0 -0
  27. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/configuration.py +0 -0
  28. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/hdx_base_configuration.yaml +0 -0
  29. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/locations.py +0 -0
  30. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/remotehdx.py +0 -0
  31. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/__init__.py +0 -0
  32. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/date_helper.py +0 -0
  33. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/filestore_helper.py +0 -0
  34. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/hdx_error_handler.py +0 -0
  35. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/hdx_state.py +0 -0
  36. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/api/utilities/url_utils.py +0 -0
  37. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/__init__.py +0 -0
  38. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/dataset.py +0 -0
  39. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/hdxobject.py +0 -0
  40. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/organization.py +0 -0
  41. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/resource_matcher.py +0 -0
  42. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/resource_view.py +0 -0
  43. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/showcase.py +0 -0
  44. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/user.py +0 -0
  45. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/data/vocabulary.py +0 -0
  46. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/__init__.py +0 -0
  47. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/infer_arguments.py +0 -0
  48. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/keyword_arguments.py +0 -0
  49. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/src/hdx/facades/simple.py +0 -0
  50. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/__init__.py +0 -0
  51. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/Accepted_Tags.csv +0 -0
  52. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/CKAN/hdx_dataset_static.yaml +0 -0
  53. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/Tag_Mapping.csv +0 -0
  54. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/Tag_Mapping_ChainRuleError.csv +0 -0
  55. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/empty.yaml +0 -0
  56. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_base_config.json +0 -0
  57. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_base_config.yaml +0 -0
  58. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_config.json +0 -0
  59. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_config.yaml +0 -0
  60. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_dataset_static.json +0 -0
  61. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_dataset_static.yaml +0 -0
  62. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_datasource_topline.json +0 -0
  63. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_datasource_topline.yaml +0 -0
  64. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_email_configuration.json +0 -0
  65. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_email_configuration.yaml +0 -0
  66. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_missing_site_config.json +0 -0
  67. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_organization_static.json +0 -0
  68. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_organization_static.yaml +0 -0
  69. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_static.json +0 -0
  70. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_static.yaml +0 -0
  71. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_view_static.json +0 -0
  72. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_resource_view_static.yaml +0 -0
  73. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_showcase_static.json +0 -0
  74. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_showcase_static.yaml +0 -0
  75. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_user_static.json +0 -0
  76. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_user_static.yaml +0 -0
  77. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_vocabulary_static.json +0 -0
  78. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/hdx_vocabulary_static.yaml +0 -0
  79. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/project_configuration.json +0 -0
  80. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/project_configuration.yaml +0 -0
  81. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config.yaml +0 -0
  82. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config2.yaml +0 -0
  83. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config3.yaml +0 -0
  84. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/config/user_agent_config_wrong.yaml +0 -0
  85. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/dataset_search_results.yaml +0 -0
  86. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/conflict_data_alg.csv +0 -0
  87. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/min_qc_conflict_data_alg.csv +0 -0
  88. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/qc_conflict_data_alg.csv +0 -0
  89. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/test_data_no_data.csv +0 -0
  90. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/download_gen_resource/test_data_no_years.csv +0 -0
  91. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/empty.csv +0 -0
  92. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/conflict_data_alg.csv +0 -0
  93. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/min_qc_conflict_data_alg.csv +0 -0
  94. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/qc_conflict_data_alg.csv +0 -0
  95. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/test_data_no_data.csv +0 -0
  96. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/gen_resource/test_data_no_years.csv +0 -0
  97. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/organization/organization_list_all_fields.yaml +0 -0
  98. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/organization/organization_show_results.yaml +0 -0
  99. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/qc_from_rows/qc_conflict_data_alg.csv +0 -0
  100. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/qc_from_rows/qc_conflict_data_alg_one_col.csv +0 -0
  101. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/resource_formats.json +0 -0
  102. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/showcase_all_search_results.yaml +0 -0
  103. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/size_hash/ACLED-All-Africa-File_20170101-to-20170708.xlsx +0 -0
  104. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/state/analysis_dates.txt +0 -0
  105. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/state/last_build_date.txt +0 -0
  106. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/test_data.csv +0 -0
  107. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/test_data.zip +0 -0
  108. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/dem_data_zwe.csv +0 -0
  109. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/dem_indicatorlist_zwe.csv +0 -0
  110. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/expected_resources_to_update.json +0 -0
  111. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/opri_data_zwe.csv +0 -0
  112. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/opri_indicatorlist_zwe.csv +0 -0
  113. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/opri_metadata_zwe.csv +0 -0
  114. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/qc_sdg_data_zwe.csv +0 -0
  115. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/sdg_data_zwe.csv +0 -0
  116. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/sdg_indicatorlist_zwe.csv +0 -0
  117. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/sdg_metadata_zwe.csv +0 -0
  118. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/unesco_dataset.json +0 -0
  119. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_dataset_resources/unesco_update_dataset.json +0 -0
  120. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_logic/update_logic_resources.yaml +0 -0
  121. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/fixtures/update_logic/update_logic_resources_new.yaml +0 -0
  122. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/__init__.py +0 -0
  123. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/__init__.py +0 -0
  124. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/test_ckan.py +0 -0
  125. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/test_configuration.py +0 -0
  126. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/test_locations.py +0 -0
  127. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/__init__.py +0 -0
  128. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_filestore_helper.py +0 -0
  129. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_hdx_error_handler.py +0 -0
  130. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/api/utilities/test_url_utils.py +0 -0
  131. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/conftest.py +0 -0
  132. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/__init__.py +0 -0
  133. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_dataset_resource_generation.py +0 -0
  134. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_update_dataset_resources.py +0 -0
  135. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/data/test_update_logic.py +0 -0
  136. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/__init__.py +0 -0
  137. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/test_infer_arguments.py +0 -0
  138. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/test_keyword_arguments.py +0 -0
  139. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/tests/hdx/facades/test_simple.py +0 -0
  140. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/my_code.py +0 -0
  141. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/my_resource.csv +0 -0
  142. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/my_resource.xlsx +0 -0
  143. {hdx_python_api-6.6.7 → hdx_python_api-6.6.8}/workingexample/run.py +0 -0
@@ -1,5 +1,5 @@
1
1
  default_language_version:
2
- python: python3.13
2
+ python: python3
3
3
 
4
4
  repos:
5
5
  - repo: https://github.com/pre-commit/pre-commit-hooks
@@ -0,0 +1,57 @@
1
+ # CLAUDE.md
2
+
3
+ ## Project Overview
4
+
5
+ **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.
6
+
7
+ ## Source Layout
8
+
9
+ - `src/hdx/api/` — Configuration, session management, locations, utilities
10
+ - `configuration.py` — `Configuration` class (credentials, site URL, user agent)
11
+ - `remotehdx.py` — Low-level CKAN API client
12
+ - `src/hdx/data/` — Data model objects, all inheriting from `HDXObject`
13
+ - `dataset.py`, `resource.py`, `organization.py`, `user.py`, `vocabulary.py`, `showcase.py`, `resource_view.py`
14
+ - `hdxobject.py` — Base class providing `_read_from_hdx` / `_write_to_hdx` and CRUD scaffolding
15
+ - `src/hdx/facades/` — High-level entry-point helpers (`simple`, `keyword_arguments`, `infer_arguments`)
16
+ - `tests/hdx/` — Test suite mirroring src layout; fixtures in `tests/fixtures/`
17
+
18
+ ## Running Tests
19
+
20
+ ```bash
21
+ uv run pytest
22
+ ```
23
+
24
+ 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.
25
+
26
+ Coverage is written to `coverage.lcov` and JUnit XML to `test-results.xml`.
27
+
28
+ ## Code Style
29
+
30
+ Formatted and linted with `ruff` (rules: E, F, I, UP; line-length not enforced). Run before committing:
31
+
32
+ ```bash
33
+ pre-commit run --all-files
34
+ ```
35
+
36
+ - Python ≥ 3.10
37
+ - Type hints throughout; use `X | Y` union syntax (PEP 604), not `Optional`/`Union`
38
+ - Google-style docstrings with `Args:` and `Returns:` sections
39
+ - No inline comments unless the *why* is non-obvious
40
+
41
+ ## Key Patterns
42
+
43
+ **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.
44
+
45
+ **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()`.
46
+
47
+ **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`).
48
+
49
+ ## Collaboration Style
50
+
51
+ - Be objective, not agreeable. Act as a partner, not a sycophant. Push back when you disagree, flag tradeoffs honestly, and don't sugarcoat problems.
52
+ - Keep explanations brief and to the point.
53
+ - 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.
54
+
55
+ ## Scope of Changes
56
+
57
+ 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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hdx-python-api
3
- Version: 6.6.7
3
+ Version: 6.6.8
4
4
  Summary: HDX Python API for interacting with the Humanitarian Data Exchange
5
5
  Project-URL: Homepage, https://github.com/OCHA-DAP/hdx-python-api
6
6
  Author: Michael Rans
@@ -25,7 +25,7 @@ Classifier: Programming Language :: Python :: 3.13
25
25
  Classifier: Programming Language :: Python :: 3.14
26
26
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
27
27
  Requires-Python: >=3.10
28
- Requires-Dist: ckanapi>=4.8
28
+ Requires-Dist: ckanapi>=4.11
29
29
  Requires-Dist: defopt>=7.0.0
30
30
  Requires-Dist: email-validator
31
31
  Requires-Dist: hdx-python-country>=4.1.1
@@ -36,7 +36,7 @@ dynamic = ["version"]
36
36
  requires-python = ">=3.10"
37
37
 
38
38
  dependencies = [
39
- "ckanapi>=4.8",
39
+ "ckanapi>=4.11",
40
40
  "defopt>=7.0.0",
41
41
  "email_validator",
42
42
  "hdx-python-country>=4.1.1",
@@ -18,7 +18,7 @@ version_tuple: tuple[int | str, ...]
18
18
  commit_id: str | None
19
19
  __commit_id__: str | None
20
20
 
21
- __version__ = version = '6.6.7'
22
- __version_tuple__ = version_tuple = (6, 6, 7)
21
+ __version__ = version = '6.6.8'
22
+ __version_tuple__ = version_tuple = (6, 6, 8)
23
23
 
24
24
  __commit_id__ = commit_id = None
@@ -62,6 +62,9 @@ class Resource(HDXObject):
62
62
  "search": "resource_search",
63
63
  "broken": "hdx_mark_broken_link_in_resource",
64
64
  "datastore_delete": "datastore_delete",
65
+ "datastore_create": "datastore_create",
66
+ "datastore_insert": "datastore_insert",
67
+ "datastore_upsert": "datastore_upsert",
65
68
  "datastore_search": "datastore_search",
66
69
  }
67
70
 
@@ -701,6 +704,53 @@ class Resource(HDXObject):
701
704
  return True
702
705
  return False
703
706
 
707
+ def create_datastore(
708
+ self,
709
+ schema: Sequence[dict],
710
+ primary_key: str | Sequence[str] | None = None,
711
+ ) -> None:
712
+ """Create a datastore for the resource with the given schema.
713
+
714
+ Args:
715
+ schema: Sequence of field definitions, each a dict with 'id' and 'type' keys.
716
+ primary_key: Primary key field name(s). Defaults to None.
717
+
718
+ Returns:
719
+ None
720
+ """
721
+ data: dict = {
722
+ "resource_id": self.data["id"],
723
+ "force": True,
724
+ "fields": schema,
725
+ }
726
+ if primary_key is not None:
727
+ if not isinstance(primary_key, str):
728
+ primary_key = ",".join(primary_key)
729
+ data["primary_key"] = primary_key
730
+ self._write_to_hdx("datastore_create", data, "resource_id")
731
+
732
+ def update_datastore(
733
+ self,
734
+ records: Sequence[dict],
735
+ method: str = "upsert",
736
+ ) -> None:
737
+ """Update (upsert) records into the resource datastore.
738
+
739
+ Args:
740
+ records: Sequence of record dicts to insert or update.
741
+ method: Datastore update method ('upsert', 'insert', or 'update'). Defaults to 'upsert'.
742
+
743
+ Returns:
744
+ None
745
+ """
746
+ data = {
747
+ "resource_id": self.data["id"],
748
+ "force": True,
749
+ "method": method,
750
+ "records": records,
751
+ }
752
+ self._write_to_hdx("datastore_upsert", data, "resource_id")
753
+
704
754
  def delete_datastore(self) -> None:
705
755
  """Delete a resource from the HDX datastore
706
756
 
@@ -47,7 +47,7 @@ class TestState:
47
47
  def do_state(self, tempfolder, statefile):
48
48
  class MockSession:
49
49
  @staticmethod
50
- def post(url, data, headers, files, allow_redirects, auth=None):
50
+ def post(url, data, **kwargs):
51
51
  if "resource" in url:
52
52
  result = json.dumps(resultdict)
53
53
  return MockResponse(
@@ -71,7 +71,7 @@ class TestState:
71
71
  def do_state_multi(self, tempfolder, multidatestatefile):
72
72
  class MockSession:
73
73
  @staticmethod
74
- def post(url, data, headers, files, allow_redirects, auth=None):
74
+ def post(url, data, **kwargs):
75
75
  if "resource" in url:
76
76
  result = json.dumps(resultdict)
77
77
  return MockResponse(
@@ -13,7 +13,7 @@ class TestDatasetAddHAPIError:
13
13
  def hapi_resource_update(self):
14
14
  class MockSession:
15
15
  @staticmethod
16
- def post(url, data, headers, files, allow_redirects, auth=None):
16
+ def post(url, data, **kwargs):
17
17
  datadict = json.loads(data.decode("utf-8"))
18
18
  if "show" in url:
19
19
  resource_id = datadict["id"]
@@ -182,7 +182,7 @@ class TestDatasetCore:
182
182
  def read(self):
183
183
  class MockSession:
184
184
  @staticmethod
185
- def post(url, data, headers, files, allow_redirects, auth=None):
185
+ def post(url, data, **kwargs):
186
186
  datadict = json.loads(data.decode("utf-8"))
187
187
  return dataset_mockshow(url, datadict)
188
188
 
@@ -192,7 +192,7 @@ class TestDatasetCore:
192
192
  def post_revise(self):
193
193
  class MockSession:
194
194
  @staticmethod
195
- def post(url, data, headers, files, allow_redirects, auth=None):
195
+ def post(url, data, **kwargs):
196
196
  if isinstance(data, dict):
197
197
  datadict = {
198
198
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -219,7 +219,7 @@ class TestDatasetCore:
219
219
  def post_create(self):
220
220
  class MockSession:
221
221
  @staticmethod
222
- def post(url, data, headers, files, allow_redirects, auth=None):
222
+ def post(url, data, **kwargs):
223
223
  if isinstance(data, dict):
224
224
  datadict = {
225
225
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -286,7 +286,7 @@ class TestDatasetCore:
286
286
  def post_update(self):
287
287
  class MockSession:
288
288
  @staticmethod
289
- def post(url, data, headers, files, allow_redirects, auth=None):
289
+ def post(url, data, **kwargs):
290
290
  if isinstance(data, dict):
291
291
  datadict = {
292
292
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -363,7 +363,7 @@ class TestDatasetCore:
363
363
  def post_reorder(self):
364
364
  class MockSession:
365
365
  @staticmethod
366
- def post(url, data, headers, files, allow_redirects, auth=None):
366
+ def post(url, data, **kwargs):
367
367
  decodedata = data.decode("utf-8")
368
368
  datadict = json.loads(decodedata)
369
369
  if "show" in url:
@@ -390,7 +390,7 @@ class TestDatasetCore:
390
390
  def post_delete(self):
391
391
  class MockSession:
392
392
  @staticmethod
393
- def post(url, data, headers, files, allow_redirects, auth=None):
393
+ def post(url, data, **kwargs):
394
394
  decodedata = data.decode("utf-8")
395
395
  datadict = json.loads(decodedata)
396
396
  if "show" in url:
@@ -423,7 +423,7 @@ class TestDatasetCore:
423
423
  def search(self):
424
424
  class MockSession:
425
425
  @staticmethod
426
- def post(url, data, headers, files, allow_redirects, auth=None):
426
+ def post(url, data, **kwargs):
427
427
  datadict = json.loads(data.decode("utf-8"))
428
428
  return mocksearch(url, datadict)
429
429
 
@@ -433,7 +433,7 @@ class TestDatasetCore:
433
433
  def post_list(self):
434
434
  class MockSession:
435
435
  @staticmethod
436
- def post(url, data, headers, files, allow_redirects, auth=None):
436
+ def post(url, data, **kwargs):
437
437
  datadict = json.loads(data.decode("utf-8"))
438
438
  return mocklist(url, datadict)
439
439
 
@@ -443,7 +443,7 @@ class TestDatasetCore:
443
443
  def all(self):
444
444
  class MockSession:
445
445
  @staticmethod
446
- def post(url, data, headers, files, allow_redirects, auth=None):
446
+ def post(url, data, **kwargs):
447
447
  datadict = json.loads(data.decode("utf-8"))
448
448
  return mockall(url, datadict)
449
449
 
@@ -453,7 +453,7 @@ class TestDatasetCore:
453
453
  def post_autocomplete(self):
454
454
  class MockSession:
455
455
  @staticmethod
456
- def post(url, data, headers, files, allow_redirects, auth=None):
456
+ def post(url, data, **kwargs):
457
457
  decodedata = data.decode("utf-8")
458
458
  datadict = json.loads(decodedata)
459
459
  if "autocomplete" not in url or "acled" not in datadict["q"]:
@@ -52,7 +52,7 @@ class TestDatasetNoncore:
52
52
 
53
53
  class MockSession:
54
54
  @staticmethod
55
- def post(url, data, headers, files, allow_redirects, auth=None):
55
+ def post(url, data, **kwargs):
56
56
  datadict = json.loads(data.decode("utf-8"))
57
57
  return vocabulary_mockshow(url, datadict)
58
58
 
@@ -62,7 +62,7 @@ class TestDatasetNoncore:
62
62
  def user_read(self):
63
63
  class MockSession:
64
64
  @staticmethod
65
- def post(url, data, headers, files, allow_redirects, auth=None):
65
+ def post(url, data, **kwargs):
66
66
  datadict = json.loads(data.decode("utf-8"))
67
67
  return user_mockshow(url, datadict)
68
68
 
@@ -72,7 +72,7 @@ class TestDatasetNoncore:
72
72
  def organization_read(self):
73
73
  class MockSession:
74
74
  @staticmethod
75
- def post(url, data, headers, files, allow_redirects, auth=None):
75
+ def post(url, data, **kwargs):
76
76
  datadict = json.loads(data.decode("utf-8"))
77
77
  return organization_mockshow(url, datadict)
78
78
 
@@ -82,7 +82,7 @@ class TestDatasetNoncore:
82
82
  def showcase_read(self):
83
83
  class MockSession:
84
84
  @staticmethod
85
- def post(url, data, headers, files, allow_redirects, auth=None):
85
+ def post(url, data, **kwargs):
86
86
  datadict = json.loads(data.decode("utf-8"))
87
87
  if "showcase_list" in url:
88
88
  result = json.dumps([showcase_resultdict])
@@ -111,7 +111,7 @@ class TestDatasetNoncore:
111
111
  def vocabulary_update(self):
112
112
  class MockSession:
113
113
  @staticmethod
114
- def post(url, data, headers, files, allow_redirects, auth=None):
114
+ def post(url, data, **kwargs):
115
115
  if isinstance(data, dict):
116
116
  datadict = {
117
117
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -115,7 +115,7 @@ class TestOrganization:
115
115
  def read(self):
116
116
  class MockSession:
117
117
  @staticmethod
118
- def post(url, data, headers, files, allow_redirects, auth=None):
118
+ def post(url, data, **kwargs):
119
119
  datadict = json.loads(data.decode("utf-8"))
120
120
  return organization_mockshow(url, datadict)
121
121
 
@@ -125,7 +125,7 @@ class TestOrganization:
125
125
  def post_create(self):
126
126
  class MockSession:
127
127
  @staticmethod
128
- def post(url, data, headers, files, allow_redirects, auth=None):
128
+ def post(url, data, **kwargs):
129
129
  datadict = json.loads(data.decode("utf-8"))
130
130
  if "show" in url:
131
131
  return organization_mockshow(url, datadict)
@@ -165,7 +165,7 @@ class TestOrganization:
165
165
  def post_update(self):
166
166
  class MockSession:
167
167
  @staticmethod
168
- def post(url, data, headers, files, allow_redirects, auth=None):
168
+ def post(url, data, **kwargs):
169
169
  datadict = json.loads(data.decode("utf-8"))
170
170
  if "show" in url:
171
171
  return organization_mockshow(url, datadict)
@@ -205,7 +205,7 @@ class TestOrganization:
205
205
  def post_delete(self):
206
206
  class MockSession:
207
207
  @staticmethod
208
- def post(url, data, headers, files, allow_redirects, auth=None):
208
+ def post(url, data, **kwargs):
209
209
  decodedata = data.decode("utf-8")
210
210
  datadict = json.loads(decodedata)
211
211
  if "show" in url:
@@ -232,7 +232,7 @@ class TestOrganization:
232
232
  def post_list(self):
233
233
  class MockSession:
234
234
  @staticmethod
235
- def post(url, data, headers, files, allow_redirects, auth=None):
235
+ def post(url, data, **kwargs):
236
236
  json.loads(data.decode("utf-8"))
237
237
  return mocklist(url)
238
238
 
@@ -242,7 +242,7 @@ class TestOrganization:
242
242
  def post_all_fields(self, fixturesfolder):
243
243
  class MockSession:
244
244
  @staticmethod
245
- def post(url, data, headers, files, allow_redirects, auth=None):
245
+ def post(url, data, **kwargs):
246
246
  kwargs = json.loads(data.decode("utf-8"))
247
247
  if "show" in url:
248
248
  return organization_mockshow(url, kwargs)
@@ -264,7 +264,7 @@ class TestOrganization:
264
264
  def user_read(self):
265
265
  class MockSession:
266
266
  @staticmethod
267
- def post(url, data, headers, files, allow_redirects, auth=None):
267
+ def post(url, data, **kwargs):
268
268
  datadict = json.loads(data.decode("utf-8"))
269
269
  return user_mockshow(url, datadict)
270
270
 
@@ -274,7 +274,7 @@ class TestOrganization:
274
274
  def datasets_get(self):
275
275
  class MockSession:
276
276
  @staticmethod
277
- def post(url, data, headers, files, allow_redirects, auth=None):
277
+ def post(url, data, **kwargs):
278
278
  datadict = json.loads(data.decode("utf-8"))
279
279
  return mockgetdatasets(url, datadict)
280
280
 
@@ -284,7 +284,7 @@ class TestOrganization:
284
284
  def post_autocomplete(self):
285
285
  class MockSession:
286
286
  @staticmethod
287
- def post(url, data, headers, files, allow_redirects, auth=None):
287
+ def post(url, data, **kwargs):
288
288
  decodedata = data.decode("utf-8")
289
289
  datadict = json.loads(decodedata)
290
290
  if "autocomplete" not in url or "innago" not in datadict["q"]:
@@ -359,7 +359,7 @@ class TestResource:
359
359
  def read(self):
360
360
  class MockSession:
361
361
  @staticmethod
362
- def post(url, data, headers, files, allow_redirects, auth=None):
362
+ def post(url, data, **kwargs):
363
363
  datadict = json.loads(data.decode("utf-8"))
364
364
  return mockshow(url, datadict)
365
365
 
@@ -369,7 +369,8 @@ class TestResource:
369
369
  def post_create(self):
370
370
  class MockSession:
371
371
  @staticmethod
372
- def post(url, data, headers, files, allow_redirects, auth=None):
372
+ def post(url, data, **kwargs):
373
+ files = kwargs.get("files")
373
374
  if isinstance(data, dict):
374
375
  datadict = {
375
376
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -431,7 +432,8 @@ class TestResource:
431
432
  def post_update(self):
432
433
  class MockSession:
433
434
  @staticmethod
434
- def post(url, data, headers, files, allow_redirects, auth=None):
435
+ def post(url, data, **kwargs):
436
+ files = kwargs.get("files")
435
437
  if isinstance(data, dict):
436
438
  datadict = {
437
439
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -491,7 +493,7 @@ class TestResource:
491
493
  def post_delete(self):
492
494
  class MockSession:
493
495
  @staticmethod
494
- def post(url, data, headers, files, allow_redirects, auth=None):
496
+ def post(url, data, **kwargs):
495
497
  decodedata = data.decode("utf-8")
496
498
  datadict = json.loads(decodedata)
497
499
  if "show" in url:
@@ -518,7 +520,7 @@ class TestResource:
518
520
  def post_datastore(self):
519
521
  class MockSession:
520
522
  @staticmethod
521
- def post(url, data, headers, files, allow_redirects, auth=None):
523
+ def post(url, data, **kwargs):
522
524
  decodedata = data.decode("utf-8")
523
525
  datadict = json.loads(decodedata)
524
526
  if "show" in url:
@@ -574,11 +576,18 @@ class TestResource:
574
576
  '{"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}}',
575
577
  )
576
578
  if (
577
- "create" in url
578
- or "insert" in url
579
- or "upsert" in url
580
- or "search" in url
581
- ) and datadict["resource_id"] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5":
579
+ "upsert" in url
580
+ and datadict["resource_id"]
581
+ == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5"
582
+ ):
583
+ TestResource.datastore = "upsert"
584
+ return MockResponse(
585
+ 200,
586
+ '{"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"}',
587
+ )
588
+ if ("create" in url or "insert" in url or "search" in url) and datadict[
589
+ "resource_id"
590
+ ] == "de6549d8-268b-4dfe-adaf-a4ae5c8510d5":
582
591
  TestResource.datastore = "create"
583
592
  return MockResponse(
584
593
  200,
@@ -595,7 +604,7 @@ class TestResource:
595
604
  def post_patch(self):
596
605
  class MockSession:
597
606
  @staticmethod
598
- def post(url, data, headers, files, allow_redirects, auth=None):
607
+ def post(url, data, **kwargs):
599
608
  datadict = json.loads(data.decode("utf-8"))
600
609
  if "show" in url:
601
610
  return mockshow(url, datadict)
@@ -607,7 +616,7 @@ class TestResource:
607
616
  def post_dataset(self):
608
617
  class MockSession:
609
618
  @staticmethod
610
- def post(url, data, headers, files, allow_redirects, auth=None):
619
+ def post(url, data, **kwargs):
611
620
  datadict = json.loads(data.decode("utf-8"))
612
621
  return mockdataset(url, datadict)
613
622
 
@@ -617,7 +626,7 @@ class TestResource:
617
626
  def search(self):
618
627
  class MockSession:
619
628
  @staticmethod
620
- def post(url, data, headers, files, allow_redirects, auth=None):
629
+ def post(url, data, **kwargs):
621
630
  datadict = json.loads(data.decode("utf-8"))
622
631
  return mocksearch(url, datadict)
623
632
 
@@ -627,7 +636,7 @@ class TestResource:
627
636
  def post_resourceview(self):
628
637
  class MockSession:
629
638
  @staticmethod
630
- def post(url, data, headers, files, allow_redirects, auth=None):
639
+ def post(url, data, **kwargs):
631
640
  decodedata = data.decode("utf-8")
632
641
  return mockresourceview(url, decodedata)
633
642
 
@@ -637,7 +646,7 @@ class TestResource:
637
646
  def post_broken(self):
638
647
  class MockSession:
639
648
  @staticmethod
640
- def post(url, data, headers, files, allow_redirects, auth=None):
649
+ def post(url, data, **kwargs):
641
650
  if isinstance(data, dict):
642
651
  datadict = {
643
652
  k.decode("utf8"): v.decode("utf8") for k, v in data.items()
@@ -977,6 +986,29 @@ class TestResource:
977
986
  TestResource.datastore = None
978
987
  assert resource2.has_datastore() is False
979
988
 
989
+ schema = [{"id": "code", "type": "text"}, {"id": "value", "type": "float"}]
990
+ records = [{"code": "A", "value": 1.0}, {"code": "B", "value": 2.0}]
991
+
992
+ TestResource.datastore = None
993
+ resource.create_datastore(schema, primary_key="code")
994
+ assert TestResource.datastore == "create"
995
+
996
+ TestResource.datastore = None
997
+ resource.create_datastore(schema, primary_key=["code", "value"])
998
+ assert TestResource.datastore == "create"
999
+
1000
+ TestResource.datastore = None
1001
+ resource.create_datastore(schema)
1002
+ assert TestResource.datastore == "create"
1003
+
1004
+ TestResource.datastore = None
1005
+ resource.update_datastore(records)
1006
+ assert TestResource.datastore == "upsert"
1007
+
1008
+ TestResource.datastore = None
1009
+ resource.update_datastore(records, method="insert")
1010
+ assert TestResource.datastore == "upsert"
1011
+
980
1012
  def test_resource_views(self, configuration, post_resourceview):
981
1013
  resource = Resource({"id": "25982d1c-f45a-45e1-b14e-87d367413045"})
982
1014
  with pytest.raises(HDXError):
@@ -136,7 +136,7 @@ class TestResourceView:
136
136
  def read(self):
137
137
  class MockSession:
138
138
  @staticmethod
139
- def post(url, data, headers, files, allow_redirects, auth=None):
139
+ def post(url, data, **kwargs):
140
140
  datadict = json.loads(data.decode("utf-8"))
141
141
  return resource_view_mockshow(url, datadict)
142
142
 
@@ -146,7 +146,7 @@ class TestResourceView:
146
146
  def post_create(self):
147
147
  class MockSession:
148
148
  @staticmethod
149
- def post(url, data, headers, files, allow_redirects, auth=None):
149
+ def post(url, data, **kwargs):
150
150
  datadict = json.loads(data.decode("utf-8"))
151
151
  if "show" in url:
152
152
  return resource_view_mockshow(url, datadict)
@@ -165,7 +165,7 @@ class TestResourceView:
165
165
  def post_update(self):
166
166
  class MockSession:
167
167
  @staticmethod
168
- def post(url, data, headers, files, allow_redirects, auth=None):
168
+ def post(url, data, **kwargs):
169
169
  datadict = json.loads(data.decode("utf-8"))
170
170
  if "show" in url:
171
171
  return resource_view_mockshow(url, datadict)
@@ -207,7 +207,7 @@ class TestResourceView:
207
207
  def post_delete(self):
208
208
  class MockSession:
209
209
  @staticmethod
210
- def post(url, data, headers, files, allow_redirects, auth=None):
210
+ def post(url, data, **kwargs):
211
211
  decodedata = data.decode("utf-8")
212
212
  datadict = json.loads(decodedata)
213
213
  if "show" in url:
@@ -234,7 +234,7 @@ class TestResourceView:
234
234
  def post_list(self):
235
235
  class MockSession:
236
236
  @staticmethod
237
- def post(url, data, headers, files, allow_redirects, auth=None):
237
+ def post(url, data, **kwargs):
238
238
  datadict = json.loads(data.decode("utf-8"))
239
239
  return resource_view_mocklist(url, datadict)
240
240
 
@@ -185,7 +185,7 @@ class TestShowcase:
185
185
  def read(self):
186
186
  class MockSession:
187
187
  @staticmethod
188
- def post(url, data, headers, files, allow_redirects, auth=None):
188
+ def post(url, data, **kwargs):
189
189
  datadict = json.loads(data.decode("utf-8"))
190
190
  if "association_delete" in url:
191
191
  TestShowcase.association = "delete"
@@ -208,7 +208,7 @@ class TestShowcase:
208
208
  def post_create(self):
209
209
  class MockSession:
210
210
  @staticmethod
211
- def post(url, data, headers, files, allow_redirects, auth=None):
211
+ def post(url, data, **kwargs):
212
212
  datadict = json.loads(data.decode("utf-8"))
213
213
  if "vocabulary" in url:
214
214
  return vocabulary_mockshow(url, datadict)
@@ -253,7 +253,7 @@ class TestShowcase:
253
253
 
254
254
  class MockSession:
255
255
  @staticmethod
256
- def post(url, data, headers, files, allow_redirects, auth=None):
256
+ def post(url, data, **kwargs):
257
257
  datadict = json.loads(data.decode("utf-8"))
258
258
  if "vocabulary" in url:
259
259
  return vocabulary_mockshow(url, datadict)
@@ -295,7 +295,7 @@ class TestShowcase:
295
295
  def post_delete(self):
296
296
  class MockSession:
297
297
  @staticmethod
298
- def post(url, data, headers, files, allow_redirects, auth=None):
298
+ def post(url, data, **kwargs):
299
299
  decodedata = data.decode("utf-8")
300
300
  datadict = json.loads(decodedata)
301
301
  if url.endswith("show") or "list" in url:
@@ -322,7 +322,7 @@ class TestShowcase:
322
322
  def allsearch(self):
323
323
  class MockSession:
324
324
  @staticmethod
325
- def post(url, data, headers, files, allow_redirects, auth=None):
325
+ def post(url, data, **kwargs):
326
326
  datadict = json.loads(data.decode("utf-8"))
327
327
  return mockallsearch(url, datadict)
328
328