rhinestone 0.1.0__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 (64) hide show
  1. rhinestone-0.1.0/LICENSE +21 -0
  2. rhinestone-0.1.0/PKG-INFO +140 -0
  3. rhinestone-0.1.0/README.md +113 -0
  4. rhinestone-0.1.0/pyproject.toml +87 -0
  5. rhinestone-0.1.0/pyproject.toml.orig +73 -0
  6. rhinestone-0.1.0/src/rhinestone/__init__.py +60 -0
  7. rhinestone-0.1.0/src/rhinestone/_http.py +152 -0
  8. rhinestone-0.1.0/src/rhinestone/adapters/__init__.py +33 -0
  9. rhinestone-0.1.0/src/rhinestone/adapters/execution/__init__.py +17 -0
  10. rhinestone-0.1.0/src/rhinestone/adapters/execution/_resource.py +14 -0
  11. rhinestone-0.1.0/src/rhinestone/adapters/execution/base.py +47 -0
  12. rhinestone-0.1.0/src/rhinestone/adapters/execution/gdal/README.md +10 -0
  13. rhinestone-0.1.0/src/rhinestone/adapters/execution/gdal/__init__.py +114 -0
  14. rhinestone-0.1.0/src/rhinestone/adapters/execution/json_service/README.md +10 -0
  15. rhinestone-0.1.0/src/rhinestone/adapters/execution/json_service/__init__.py +103 -0
  16. rhinestone-0.1.0/src/rhinestone/adapters/execution/pyogrio/README.md +10 -0
  17. rhinestone-0.1.0/src/rhinestone/adapters/execution/pyogrio/__init__.py +44 -0
  18. rhinestone-0.1.0/src/rhinestone/adapters/execution/rasterio/README.md +9 -0
  19. rhinestone-0.1.0/src/rhinestone/adapters/execution/rasterio/__init__.py +38 -0
  20. rhinestone-0.1.0/src/rhinestone/adapters/source/__init__.py +29 -0
  21. rhinestone-0.1.0/src/rhinestone/adapters/source/_knowledge.py +59 -0
  22. rhinestone-0.1.0/src/rhinestone/adapters/source/_uri.py +20 -0
  23. rhinestone-0.1.0/src/rhinestone/adapters/source/base.py +234 -0
  24. rhinestone-0.1.0/src/rhinestone/adapters/source/ckan/README.md +12 -0
  25. rhinestone-0.1.0/src/rhinestone/adapters/source/ckan/__init__.py +176 -0
  26. rhinestone-0.1.0/src/rhinestone/adapters/source/ckan/schema.json +11 -0
  27. rhinestone-0.1.0/src/rhinestone/adapters/source/dcat/README.md +12 -0
  28. rhinestone-0.1.0/src/rhinestone/adapters/source/dcat/__init__.py +178 -0
  29. rhinestone-0.1.0/src/rhinestone/adapters/source/dcat/schema.json +13 -0
  30. rhinestone-0.1.0/src/rhinestone/adapters/source/direct/README.md +10 -0
  31. rhinestone-0.1.0/src/rhinestone/adapters/source/direct/__init__.py +56 -0
  32. rhinestone-0.1.0/src/rhinestone/adapters/source/direct/schema.json +17 -0
  33. rhinestone-0.1.0/src/rhinestone/adapters/source/gsi_fundamental/README.md +11 -0
  34. rhinestone-0.1.0/src/rhinestone/adapters/source/gsi_fundamental/__init__.py +43 -0
  35. rhinestone-0.1.0/src/rhinestone/adapters/source/gsi_fundamental/schema.json +32 -0
  36. rhinestone-0.1.0/src/rhinestone/adapters/source/odpt/README.md +11 -0
  37. rhinestone-0.1.0/src/rhinestone/adapters/source/odpt/__init__.py +140 -0
  38. rhinestone-0.1.0/src/rhinestone/adapters/source/odpt/schema.json +15 -0
  39. rhinestone-0.1.0/src/rhinestone/adapters/source/ogc/README.md +12 -0
  40. rhinestone-0.1.0/src/rhinestone/adapters/source/ogc/__init__.py +171 -0
  41. rhinestone-0.1.0/src/rhinestone/adapters/source/ogc/schema.json +12 -0
  42. rhinestone-0.1.0/src/rhinestone/adapters/source/plateau/README.md +12 -0
  43. rhinestone-0.1.0/src/rhinestone/adapters/source/plateau/__init__.py +96 -0
  44. rhinestone-0.1.0/src/rhinestone/adapters/source/plateau/schema.json +24 -0
  45. rhinestone-0.1.0/src/rhinestone/adapters/source/search_ckan_jp/__init__.py +154 -0
  46. rhinestone-0.1.0/src/rhinestone/adapters/source/search_ckan_jp/schema.json +9 -0
  47. rhinestone-0.1.0/src/rhinestone/adapters/source/stac/README.md +11 -0
  48. rhinestone-0.1.0/src/rhinestone/adapters/source/stac/__init__.py +188 -0
  49. rhinestone-0.1.0/src/rhinestone/adapters/source/stac/schema.json +13 -0
  50. rhinestone-0.1.0/src/rhinestone/adapters/source/static/__init__.py +217 -0
  51. rhinestone-0.1.0/src/rhinestone/adapters/source/static/schema.json +10 -0
  52. rhinestone-0.1.0/src/rhinestone/api.py +396 -0
  53. rhinestone-0.1.0/src/rhinestone/catalogs/__init__.py +147 -0
  54. rhinestone-0.1.0/src/rhinestone/catalogs/sources.json +188 -0
  55. rhinestone-0.1.0/src/rhinestone/errors.py +69 -0
  56. rhinestone-0.1.0/src/rhinestone/execution.py +39 -0
  57. rhinestone-0.1.0/src/rhinestone/models.py +342 -0
  58. rhinestone-0.1.0/src/rhinestone/pipeline.py +96 -0
  59. rhinestone-0.1.0/src/rhinestone/py.typed +0 -0
  60. rhinestone-0.1.0/src/rhinestone/registry.py +99 -0
  61. rhinestone-0.1.0/src/rhinestone/resolution.py +102 -0
  62. rhinestone-0.1.0/src/rhinestone/search.py +172 -0
  63. rhinestone-0.1.0/src/rhinestone/security.py +231 -0
  64. rhinestone-0.1.0/src/rhinestone/sources.py +31 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 kitazawa
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,140 @@
1
+ Metadata-Version: 2.4
2
+ Name: rhinestone
3
+ Version: 0.1.0
4
+ Summary: Knowledge and specification layer for Japanese public and geospatial data
5
+ Keywords: geospatial,open-data,japan,data-access
6
+ Author: kitazawa
7
+ Author-email: kitazawa <88121050+U-KITAZAWA@users.noreply.github.com>
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3 :: Only
15
+ Classifier: Programming Language :: Python :: 3.10
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Typing :: Typed
20
+ Requires-Dist: jsonschema>=4.18,<5
21
+ Requires-Python: >=3.10
22
+ Project-URL: Homepage, https://github.com/u-kitazawa/rhinestone
23
+ Project-URL: Repository, https://github.com/u-kitazawa/rhinestone
24
+ Project-URL: Issues, https://github.com/u-kitazawa/rhinestone/issues
25
+ Project-URL: Documentation, https://github.com/u-kitazawa/rhinestone/tree/main/docs
26
+ Description-Content-Type: text/markdown
27
+
28
+ # Rhinestone
29
+
30
+ Rhinestoneは、日本の公的・地理空間データを探し、利用可能なResourceへ解決し、既存の専門ライブラリへ渡すPythonライブラリです。
31
+
32
+ 利用者が覚える中心概念は次の4つです。
33
+
34
+ ```text
35
+ Catalog -> Provider -> Result -> Resource
36
+ ```
37
+
38
+ - `Catalog`: Rhinestoneが知っているProviderの集合
39
+ - `Provider`: データを提供する主体・サービス
40
+ - `Result`: 検索で見つかった候補
41
+ - `Resource`: 実際に利用できる具体的なデータ
42
+
43
+ ## 対象ユーザー
44
+
45
+ Rhinestoneは、次のような利用者を対象にしています。
46
+
47
+ - 日本の行政・公的オープンデータをPythonから横断的に探索したい利用者
48
+ - GIS・リモートセンシングの研究者
49
+ - 公的データを扱うデータエンジニア・データ基盤開発者
50
+ - Rasterio、GDAL、pyogrioなどへ渡す前のProvider固有処理を共通化したい利用者
51
+
52
+ Rhinestoneは、Catalogに構成されたProvider内のデータを検索してResultとして発見し、Resourceへ解決することを担当します。実際のGIS処理やデータ解析は既存の専門ライブラリへ委譲します。
53
+
54
+ ## 対象外のユースケース
55
+
56
+ RhinestoneはGIS処理ライブラリやワークフローエンジンではありません。次の用途には、そのまま利用できないか、追加の検討が必要です。
57
+
58
+ - GISの空間演算・形式変換・解析そのもの
59
+ - ETLやワークフローの実行基盤
60
+ - 外部RuntimeやProvider固有の制約を完全に隠蔽すること
61
+ - 0.1.x時点で公開APIの長期固定を前提とする本番システム
62
+
63
+ 対応するProvider、Runtime、形式の範囲は[Compatibility](docs/compatibility.md)を、Runtimeの注入方法は[Configuration](docs/configuration.md)を参照してください。RhinestoneはAlpha版のため、公開APIは今後変更される可能性があります。
64
+
65
+ ## インストール
66
+
67
+ ```console
68
+ python -m pip install rhinestone
69
+ ```
70
+
71
+ ## 基本的な使い方
72
+
73
+ ```python
74
+ from rhinestone import configure
75
+ from rhinestone.catalogs import BUILTIN
76
+
77
+ app = configure(catalog=BUILTIN)
78
+ results = app.search(text="河川")
79
+ resource = app.resolve(results[0])
80
+
81
+ print(resource.uri)
82
+ print(resource.metadata)
83
+ ```
84
+
85
+ `BUILTIN`はRhinestoneが提供する組み込みCatalogです。`search()`はCatalogに構成されたProvider内のデータを検索してResultを返し、検索結果は`app.resolve(result)`で直接Resourceへ解決できます。
86
+ 複数Provider時の整数indexingは構成したProvider順の走査用であり、Providerを横断した関連度rankingではありません。詳細は[Search resultの順序](docs/search.md#結果の順序)を参照してください。
87
+
88
+ 検索を使わず、既知のProviderを選んで構成することもできます。
89
+
90
+ ```python
91
+ from rhinestone import Catalog, Provider, configure
92
+
93
+ catalog = Catalog((
94
+ Provider(
95
+ id="my-stac",
96
+ adapter_type="stac",
97
+ settings={"endpoint": "https://stac.example/api"},
98
+ ),
99
+ ))
100
+ app = configure(catalog=catalog)
101
+ ```
102
+
103
+ ## RuntimeとCredential
104
+
105
+ GDAL、Rasterio、pyogrio、RDFLibなどの外部Runtimeは利用者が用意します。API keyやtokenはCredentialとして分離します。
106
+
107
+ ```python
108
+ app = configure(
109
+ catalog=BUILTIN,
110
+ dependencies={"rasterio": rasterio},
111
+ credentials={"odpt": lambda: odpt_consumer_key},
112
+ )
113
+ ```
114
+
115
+ HTTP通信はRhinestoneに組み込まれています。
116
+
117
+ ## 内部アーキテクチャ
118
+
119
+ 利用者が意識する必要のない`Source`、`Config`、`AccessPlan`、`Resolver`、各種Adapter、Registryは内部実装です。内部では責務分離のためにこれらの段階を保持しますが、通常の利用導線には出しません。
120
+
121
+ RhinestoneはGIS I/O、形式変換、空間演算、データ解析を実装せず、解決済みResourceを既存の専門Runtimeへ渡します。
122
+
123
+ ## ドキュメント
124
+
125
+ - [Documentation](docs/index.md)
126
+ - [Getting started](docs/getting-started.md)
127
+ - [Configuration](docs/configuration.md)
128
+ - [API reference](docs/api.md)
129
+ - [Compatibility](docs/compatibility.md)
130
+ - [Runtime guide](docs/runtimes.md)
131
+ - [API stability and release policy](docs/release-policy.md)
132
+
133
+ ## 開発環境
134
+
135
+ ```console
136
+ uv sync --dev
137
+ bash scripts/check.sh
138
+ ```
139
+
140
+ MIT Licenseです。
@@ -0,0 +1,113 @@
1
+ # Rhinestone
2
+
3
+ Rhinestoneは、日本の公的・地理空間データを探し、利用可能なResourceへ解決し、既存の専門ライブラリへ渡すPythonライブラリです。
4
+
5
+ 利用者が覚える中心概念は次の4つです。
6
+
7
+ ```text
8
+ Catalog -> Provider -> Result -> Resource
9
+ ```
10
+
11
+ - `Catalog`: Rhinestoneが知っているProviderの集合
12
+ - `Provider`: データを提供する主体・サービス
13
+ - `Result`: 検索で見つかった候補
14
+ - `Resource`: 実際に利用できる具体的なデータ
15
+
16
+ ## 対象ユーザー
17
+
18
+ Rhinestoneは、次のような利用者を対象にしています。
19
+
20
+ - 日本の行政・公的オープンデータをPythonから横断的に探索したい利用者
21
+ - GIS・リモートセンシングの研究者
22
+ - 公的データを扱うデータエンジニア・データ基盤開発者
23
+ - Rasterio、GDAL、pyogrioなどへ渡す前のProvider固有処理を共通化したい利用者
24
+
25
+ Rhinestoneは、Catalogに構成されたProvider内のデータを検索してResultとして発見し、Resourceへ解決することを担当します。実際のGIS処理やデータ解析は既存の専門ライブラリへ委譲します。
26
+
27
+ ## 対象外のユースケース
28
+
29
+ RhinestoneはGIS処理ライブラリやワークフローエンジンではありません。次の用途には、そのまま利用できないか、追加の検討が必要です。
30
+
31
+ - GISの空間演算・形式変換・解析そのもの
32
+ - ETLやワークフローの実行基盤
33
+ - 外部RuntimeやProvider固有の制約を完全に隠蔽すること
34
+ - 0.1.x時点で公開APIの長期固定を前提とする本番システム
35
+
36
+ 対応するProvider、Runtime、形式の範囲は[Compatibility](docs/compatibility.md)を、Runtimeの注入方法は[Configuration](docs/configuration.md)を参照してください。RhinestoneはAlpha版のため、公開APIは今後変更される可能性があります。
37
+
38
+ ## インストール
39
+
40
+ ```console
41
+ python -m pip install rhinestone
42
+ ```
43
+
44
+ ## 基本的な使い方
45
+
46
+ ```python
47
+ from rhinestone import configure
48
+ from rhinestone.catalogs import BUILTIN
49
+
50
+ app = configure(catalog=BUILTIN)
51
+ results = app.search(text="河川")
52
+ resource = app.resolve(results[0])
53
+
54
+ print(resource.uri)
55
+ print(resource.metadata)
56
+ ```
57
+
58
+ `BUILTIN`はRhinestoneが提供する組み込みCatalogです。`search()`はCatalogに構成されたProvider内のデータを検索してResultを返し、検索結果は`app.resolve(result)`で直接Resourceへ解決できます。
59
+ 複数Provider時の整数indexingは構成したProvider順の走査用であり、Providerを横断した関連度rankingではありません。詳細は[Search resultの順序](docs/search.md#結果の順序)を参照してください。
60
+
61
+ 検索を使わず、既知のProviderを選んで構成することもできます。
62
+
63
+ ```python
64
+ from rhinestone import Catalog, Provider, configure
65
+
66
+ catalog = Catalog((
67
+ Provider(
68
+ id="my-stac",
69
+ adapter_type="stac",
70
+ settings={"endpoint": "https://stac.example/api"},
71
+ ),
72
+ ))
73
+ app = configure(catalog=catalog)
74
+ ```
75
+
76
+ ## RuntimeとCredential
77
+
78
+ GDAL、Rasterio、pyogrio、RDFLibなどの外部Runtimeは利用者が用意します。API keyやtokenはCredentialとして分離します。
79
+
80
+ ```python
81
+ app = configure(
82
+ catalog=BUILTIN,
83
+ dependencies={"rasterio": rasterio},
84
+ credentials={"odpt": lambda: odpt_consumer_key},
85
+ )
86
+ ```
87
+
88
+ HTTP通信はRhinestoneに組み込まれています。
89
+
90
+ ## 内部アーキテクチャ
91
+
92
+ 利用者が意識する必要のない`Source`、`Config`、`AccessPlan`、`Resolver`、各種Adapter、Registryは内部実装です。内部では責務分離のためにこれらの段階を保持しますが、通常の利用導線には出しません。
93
+
94
+ RhinestoneはGIS I/O、形式変換、空間演算、データ解析を実装せず、解決済みResourceを既存の専門Runtimeへ渡します。
95
+
96
+ ## ドキュメント
97
+
98
+ - [Documentation](docs/index.md)
99
+ - [Getting started](docs/getting-started.md)
100
+ - [Configuration](docs/configuration.md)
101
+ - [API reference](docs/api.md)
102
+ - [Compatibility](docs/compatibility.md)
103
+ - [Runtime guide](docs/runtimes.md)
104
+ - [API stability and release policy](docs/release-policy.md)
105
+
106
+ ## 開発環境
107
+
108
+ ```console
109
+ uv sync --dev
110
+ bash scripts/check.sh
111
+ ```
112
+
113
+ MIT Licenseです。
@@ -0,0 +1,87 @@
1
+ [project]
2
+ name = "rhinestone"
3
+ version = "0.1.0"
4
+ description = "Knowledge and specification layer for Japanese public and geospatial data"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ requires-python = ">=3.10"
9
+ keywords = [
10
+ "geospatial",
11
+ "open-data",
12
+ "japan",
13
+ "data-access",
14
+ ]
15
+ classifiers = [
16
+ "Development Status :: 3 - Alpha",
17
+ "Intended Audience :: Developers",
18
+ "License :: OSI Approved :: MIT License",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3 :: Only",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Typing :: Typed",
26
+ ]
27
+ dependencies = ["jsonschema>=4.18,<5"]
28
+
29
+ [[project.authors]]
30
+ name = "kitazawa"
31
+ email = "88121050+U-KITAZAWA@users.noreply.github.com"
32
+
33
+ [project.urls]
34
+ Homepage = "https://github.com/u-kitazawa/rhinestone"
35
+ Repository = "https://github.com/u-kitazawa/rhinestone"
36
+ Issues = "https://github.com/u-kitazawa/rhinestone/issues"
37
+ Documentation = "https://github.com/u-kitazawa/rhinestone/tree/main/docs"
38
+
39
+ [dependency-groups]
40
+ dev = [
41
+ "rdflib>=6,<7",
42
+ "pyright>=1.1.339,<1.1.390",
43
+ "pytest>=7.4.4,<8",
44
+ "pytest-cov>=4.1,<5",
45
+ "ruff>=0.12.12,<0.13",
46
+ "mkdocs-material>=9.7.7",
47
+ ]
48
+
49
+ [tool.coverage.run]
50
+ source = ["rhinestone"]
51
+
52
+ [tool.pytest.ini_options]
53
+ addopts = [
54
+ "--cov=rhinestone",
55
+ "--cov-branch",
56
+ "--cov-report=term-missing",
57
+ "--cov-fail-under=100",
58
+ ]
59
+ testpaths = ["tests"]
60
+
61
+ [tool.ruff]
62
+ line-length = 88
63
+ target-version = "py37"
64
+
65
+ [tool.ruff.lint]
66
+ select = [
67
+ "E4",
68
+ "E7",
69
+ "E9",
70
+ "F",
71
+ "I",
72
+ "UP",
73
+ ]
74
+
75
+ [tool.pyright]
76
+ include = [
77
+ "src",
78
+ "tests",
79
+ ]
80
+ pythonVersion = "3.10"
81
+ typeCheckingMode = "strict"
82
+ venv = ".venv"
83
+ venvPath = "."
84
+
85
+ [build-system]
86
+ requires = ["uv_build>=0.12.9,<0.13.0"]
87
+ build-backend = "uv_build"
@@ -0,0 +1,73 @@
1
+ [project]
2
+ name = "rhinestone"
3
+ version = "0.1.0"
4
+ description = "Knowledge and specification layer for Japanese public and geospatial data"
5
+ readme = "README.md"
6
+ license = "MIT"
7
+ license-files = ["LICENSE"]
8
+ authors = [
9
+ { name = "kitazawa", email = "88121050+U-KITAZAWA@users.noreply.github.com" }
10
+ ]
11
+ requires-python = ">=3.10"
12
+ keywords = ["geospatial", "open-data", "japan", "data-access"]
13
+ classifiers = [
14
+ "Development Status :: 3 - Alpha",
15
+ "Intended Audience :: Developers",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3 :: Only",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Programming Language :: Python :: 3.12",
22
+ "Programming Language :: Python :: 3.13",
23
+ "Typing :: Typed",
24
+ ]
25
+ dependencies = [
26
+ "jsonschema>=4.18,<5",
27
+ ]
28
+
29
+ [project.urls]
30
+ Homepage = "https://github.com/u-kitazawa/rhinestone"
31
+ Repository = "https://github.com/u-kitazawa/rhinestone"
32
+ Issues = "https://github.com/u-kitazawa/rhinestone/issues"
33
+ Documentation = "https://github.com/u-kitazawa/rhinestone/tree/main/docs"
34
+
35
+ [dependency-groups]
36
+ dev = [
37
+ "rdflib>=6,<7",
38
+ "pyright>=1.1.339,<1.1.390",
39
+ "pytest>=7.4.4,<8",
40
+ "pytest-cov>=4.1,<5",
41
+ "ruff>=0.12.12,<0.13",
42
+ "mkdocs-material>=9.7.7",
43
+ ]
44
+
45
+ [tool.coverage.run]
46
+ source = ["rhinestone"]
47
+
48
+ [build-system]
49
+ requires = ["uv_build>=0.12.9,<0.13.0"]
50
+ build-backend = "uv_build"
51
+
52
+ [tool.pytest.ini_options]
53
+ addopts = [
54
+ "--cov=rhinestone",
55
+ "--cov-branch",
56
+ "--cov-report=term-missing",
57
+ "--cov-fail-under=100",
58
+ ]
59
+ testpaths = ["tests"]
60
+
61
+ [tool.ruff]
62
+ line-length = 88
63
+ target-version = "py37"
64
+
65
+ [tool.ruff.lint]
66
+ select = ["E4", "E7", "E9", "F", "I", "UP"]
67
+
68
+ [tool.pyright]
69
+ include = ["src", "tests"]
70
+ pythonVersion = "3.10"
71
+ typeCheckingMode = "strict"
72
+ venv = ".venv"
73
+ venvPath = "."
@@ -0,0 +1,60 @@
1
+ """Rhinestone public package."""
2
+
3
+ from . import sources
4
+ from .api import Rhinestone, configure
5
+ from .catalogs import Catalog
6
+ from .models import (
7
+ AccessPlan,
8
+ Config,
9
+ Dependencies,
10
+ FileAccessPlan,
11
+ LibraryName,
12
+ Metadata,
13
+ Provenance,
14
+ Provider,
15
+ RemoteDatasetPlan,
16
+ Resource,
17
+ ResourceCandidate,
18
+ Result,
19
+ Runtime,
20
+ RuntimeFactory,
21
+ SearchDiagnostic,
22
+ SearchQuery,
23
+ SearchResult,
24
+ ServiceQueryPlan,
25
+ Source,
26
+ SourceDefinition,
27
+ )
28
+ from .search import SearchResults
29
+ from .security import DestinationPolicy, DestinationRule, NetworkPolicyLevel
30
+
31
+ __all__ = [
32
+ "AccessPlan",
33
+ "Catalog",
34
+ "Config",
35
+ "DestinationPolicy",
36
+ "DestinationRule",
37
+ "Dependencies",
38
+ "FileAccessPlan",
39
+ "LibraryName",
40
+ "Metadata",
41
+ "NetworkPolicyLevel",
42
+ "Provenance",
43
+ "Provider",
44
+ "RemoteDatasetPlan",
45
+ "Rhinestone",
46
+ "Resource",
47
+ "ResourceCandidate",
48
+ "Result",
49
+ "Runtime",
50
+ "RuntimeFactory",
51
+ "SearchDiagnostic",
52
+ "SearchQuery",
53
+ "SearchResult",
54
+ "SearchResults",
55
+ "ServiceQueryPlan",
56
+ "Source",
57
+ "SourceDefinition",
58
+ "configure",
59
+ "sources",
60
+ ]
@@ -0,0 +1,152 @@
1
+ """Built-in standard-library HTTP transport."""
2
+
3
+ import json
4
+ from typing import Any, Mapping, Optional, cast
5
+ from urllib.error import HTTPError
6
+ from urllib.parse import urlencode, urlsplit, urlunsplit
7
+ from urllib.request import HTTPRedirectHandler, Request, build_opener, urlopen
8
+
9
+ from .errors import ProviderResponseError
10
+
11
+ _TIMEOUT_SECONDS = 30
12
+ _DEFAULT_HEADERS = {
13
+ "Accept": "application/json",
14
+ "User-Agent": "rhinestone",
15
+ }
16
+
17
+
18
+ class _JsonIntegerDecodeError(ValueError):
19
+ """JSON integer conversion failed inside the decoder."""
20
+
21
+
22
+ def _parse_json_int(value: str) -> int:
23
+ try:
24
+ return int(value)
25
+ except ValueError as error:
26
+ raise _JsonIntegerDecodeError from error
27
+
28
+
29
+ class JsonDocument(dict[str, Any]):
30
+ """Decoded JSON object together with the URI that supplied it."""
31
+
32
+ def __init__(self, value: Mapping[str, Any], response_uri: str) -> None:
33
+ super().__init__(value)
34
+ self.response_uri = response_uri
35
+
36
+
37
+ def get_json(
38
+ url: str,
39
+ params: Mapping[str, Any],
40
+ headers: Optional[Mapping[str, str]] = None,
41
+ ) -> Any:
42
+ """Fetch and decode JSON over HTTP using the standard library."""
43
+ request = _request(url, params, headers)
44
+ opener = (
45
+ build_opener(_NoRedirectHandler())
46
+ if getattr(headers, "_rhinestone_no_redirects", False)
47
+ else None
48
+ )
49
+ open_request = opener.open if opener is not None else urlopen
50
+ with open_request(request, timeout=_TIMEOUT_SECONDS) as response:
51
+ try:
52
+ decoded = json.load(response, parse_int=_parse_json_int)
53
+ except (
54
+ UnicodeDecodeError,
55
+ json.JSONDecodeError,
56
+ _JsonIntegerDecodeError,
57
+ ):
58
+ raise ProviderResponseError("Provider response is not valid JSON") from None
59
+ if isinstance(decoded, Mapping):
60
+ response_uri = getattr(response, "geturl", lambda: request.full_url)()
61
+ return JsonDocument(
62
+ cast(Mapping[str, Any], decoded), response_uri or request.full_url
63
+ )
64
+ return decoded
65
+
66
+
67
+ def get_text(url: str) -> str:
68
+ """Fetch a text document without following HTTP redirects."""
69
+ request = Request(url, headers={"User-Agent": "rhinestone"})
70
+ opener = build_opener(_NoRedirectHandler())
71
+ with opener.open(request, timeout=_TIMEOUT_SECONDS) as response:
72
+ charset = response.headers.get_content_charset() or "utf-8"
73
+ return response.read().decode(charset)
74
+
75
+
76
+ class JsonServiceRuntime:
77
+ """Small requests-compatible runtime used by ``JsonServiceAdapter``."""
78
+
79
+ def get(
80
+ self,
81
+ url: str,
82
+ *,
83
+ params: Mapping[str, Any],
84
+ headers: Mapping[str, str],
85
+ timeout: int,
86
+ allow_redirects: bool,
87
+ ) -> "JsonResponse":
88
+ request = _request(url, params, headers)
89
+ opener = build_opener(_NoRedirectHandler())
90
+ try:
91
+ response = opener.open(request, timeout=timeout)
92
+ except HTTPError as error:
93
+ response = error
94
+ with response:
95
+ charset = response.headers.get_content_charset() or "utf-8"
96
+ status_code = cast(int, response.getcode())
97
+ return JsonResponse(status_code, response.read(), charset)
98
+
99
+
100
+ class JsonResponse:
101
+ """Subset of the requests response contract used by ``JsonServiceAdapter``."""
102
+
103
+ def __init__(self, status_code: int, body: bytes, charset: str) -> None:
104
+ self.status_code = status_code
105
+ self._body = body
106
+ self._charset = charset
107
+
108
+ def raise_for_status(self) -> None:
109
+ if self.status_code >= 400:
110
+ raise OSError(f"HTTP request failed with status {self.status_code}")
111
+
112
+ def json(self) -> Any:
113
+ return json.loads(self._body.decode(self._charset))
114
+
115
+
116
+ class _NoRedirectHandler(HTTPRedirectHandler):
117
+ def redirect_request(
118
+ self,
119
+ req: Request,
120
+ fp: Any,
121
+ code: int,
122
+ msg: str,
123
+ headers: Any,
124
+ newurl: str,
125
+ ) -> Optional[Request]:
126
+ return None
127
+
128
+
129
+ def _request(
130
+ url: str,
131
+ params: Mapping[str, Any],
132
+ headers: Optional[Mapping[str, str]] = None,
133
+ ) -> Request:
134
+ request_url = _append_query(url, params)
135
+ return Request(
136
+ request_url,
137
+ headers={**_DEFAULT_HEADERS, **dict(headers or {})},
138
+ )
139
+
140
+
141
+ def _append_query(url: str, params: Mapping[str, Any]) -> str:
142
+ """Append parameters without replacing or normalizing an existing query."""
143
+ additional_query = urlencode(params, doseq=True)
144
+ if not additional_query:
145
+ return url
146
+ components = urlsplit(url)
147
+ query = (
148
+ f"{components.query}&{additional_query}"
149
+ if components.query
150
+ else additional_query
151
+ )
152
+ return urlunsplit(components._replace(query=query))
@@ -0,0 +1,33 @@
1
+ """Compatibility exports for built-in source adapters."""
2
+
3
+ from .execution import ExecutionAdapter
4
+ from .source import (
5
+ CkanAdapter,
6
+ DcatAdapter,
7
+ DirectAdapter,
8
+ GsiFundamentalAdapter,
9
+ JsonGetter,
10
+ JsonObject,
11
+ OdptAdapter,
12
+ OgcFeaturesAdapter,
13
+ PlateauAdapter,
14
+ ProviderAdapter,
15
+ StacAdapter,
16
+ StaticAdapter,
17
+ )
18
+
19
+ __all__ = [
20
+ "CkanAdapter",
21
+ "DirectAdapter",
22
+ "DcatAdapter",
23
+ "GsiFundamentalAdapter",
24
+ "JsonGetter",
25
+ "JsonObject",
26
+ "OdptAdapter",
27
+ "OgcFeaturesAdapter",
28
+ "PlateauAdapter",
29
+ "ProviderAdapter",
30
+ "StacAdapter",
31
+ "StaticAdapter",
32
+ "ExecutionAdapter",
33
+ ]