datanyx 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 (111) hide show
  1. datanyx-0.1.0/LICENSE +21 -0
  2. datanyx-0.1.0/PKG-INFO +146 -0
  3. datanyx-0.1.0/README.md +84 -0
  4. datanyx-0.1.0/pyproject.toml +130 -0
  5. datanyx-0.1.0/setup.cfg +4 -0
  6. datanyx-0.1.0/src/datanyx/__init__.py +117 -0
  7. datanyx-0.1.0/src/datanyx/__main__.py +5 -0
  8. datanyx-0.1.0/src/datanyx/audit.py +194 -0
  9. datanyx-0.1.0/src/datanyx/check/__init__.py +125 -0
  10. datanyx-0.1.0/src/datanyx/check/model.py +179 -0
  11. datanyx-0.1.0/src/datanyx/check/python_check.py +368 -0
  12. datanyx-0.1.0/src/datanyx/check/sql_check.py +328 -0
  13. datanyx-0.1.0/src/datanyx/cli.py +1220 -0
  14. datanyx-0.1.0/src/datanyx/config.py +349 -0
  15. datanyx-0.1.0/src/datanyx/diff.py +239 -0
  16. datanyx-0.1.0/src/datanyx/evaluation.py +223 -0
  17. datanyx-0.1.0/src/datanyx/fileperms.py +29 -0
  18. datanyx-0.1.0/src/datanyx/formats.py +52 -0
  19. datanyx-0.1.0/src/datanyx/inference.py +275 -0
  20. datanyx-0.1.0/src/datanyx/keys.py +231 -0
  21. datanyx-0.1.0/src/datanyx/mcp_server.py +365 -0
  22. datanyx-0.1.0/src/datanyx/privacy.py +77 -0
  23. datanyx-0.1.0/src/datanyx/protect/__init__.py +21 -0
  24. datanyx-0.1.0/src/datanyx/protect/config.py +91 -0
  25. datanyx-0.1.0/src/datanyx/protect/guard.py +345 -0
  26. datanyx-0.1.0/src/datanyx/protect/settings.py +266 -0
  27. datanyx-0.1.0/src/datanyx/pseudonymize/__init__.py +23 -0
  28. datanyx-0.1.0/src/datanyx/pseudonymize/fakes.py +246 -0
  29. datanyx-0.1.0/src/datanyx/pseudonymize/keys.py +41 -0
  30. datanyx-0.1.0/src/datanyx/pseudonymize/plan.py +204 -0
  31. datanyx-0.1.0/src/datanyx/pseudonymize/tables.py +285 -0
  32. datanyx-0.1.0/src/datanyx/renderers/__init__.py +22 -0
  33. datanyx-0.1.0/src/datanyx/renderers/json_schema.py +114 -0
  34. datanyx-0.1.0/src/datanyx/renderers/naming.py +39 -0
  35. datanyx-0.1.0/src/datanyx/renderers/pydantic.py +139 -0
  36. datanyx-0.1.0/src/datanyx/renderers/sql_ddl.py +108 -0
  37. datanyx-0.1.0/src/datanyx/renderers/text.py +80 -0
  38. datanyx-0.1.0/src/datanyx/renderers/typescript.py +90 -0
  39. datanyx-0.1.0/src/datanyx/scale.py +27 -0
  40. datanyx-0.1.0/src/datanyx/scan.py +304 -0
  41. datanyx-0.1.0/src/datanyx/schema.py +118 -0
  42. datanyx-0.1.0/src/datanyx/scrub.py +200 -0
  43. datanyx-0.1.0/src/datanyx/sources/__init__.py +73 -0
  44. datanyx-0.1.0/src/datanyx/sources/avro_source.py +141 -0
  45. datanyx-0.1.0/src/datanyx/sources/base.py +36 -0
  46. datanyx-0.1.0/src/datanyx/sources/catalog.py +43 -0
  47. datanyx-0.1.0/src/datanyx/sources/delimited_source.py +73 -0
  48. datanyx-0.1.0/src/datanyx/sources/directory.py +140 -0
  49. datanyx-0.1.0/src/datanyx/sources/duckdb_source.py +199 -0
  50. datanyx-0.1.0/src/datanyx/sources/excel_source.py +83 -0
  51. datanyx-0.1.0/src/datanyx/sources/fhir_source.py +90 -0
  52. datanyx-0.1.0/src/datanyx/sources/files.py +119 -0
  53. datanyx-0.1.0/src/datanyx/sources/hl7_source.py +109 -0
  54. datanyx-0.1.0/src/datanyx/sources/json_source.py +158 -0
  55. datanyx-0.1.0/src/datanyx/sources/mongodb_source.py +136 -0
  56. datanyx-0.1.0/src/datanyx/sources/orc_source.py +36 -0
  57. datanyx-0.1.0/src/datanyx/sources/parquet_source.py +113 -0
  58. datanyx-0.1.0/src/datanyx/sources/sql_source.py +392 -0
  59. datanyx-0.1.0/src/datanyx/sources/stats_source.py +132 -0
  60. datanyx-0.1.0/src/datanyx/sources/xml_source.py +101 -0
  61. datanyx-0.1.0/src/datanyx/sources/yaml_source.py +53 -0
  62. datanyx-0.1.0/src/datanyx/synth/__init__.py +8 -0
  63. datanyx-0.1.0/src/datanyx/synth/generator.py +328 -0
  64. datanyx-0.1.0/src/datanyx/synth/writers.py +110 -0
  65. datanyx-0.1.0/src/datanyx/text/__init__.py +29 -0
  66. datanyx-0.1.0/src/datanyx/text/anonymizer.py +285 -0
  67. datanyx-0.1.0/src/datanyx/text/backends.py +259 -0
  68. datanyx-0.1.0/src/datanyx/text/batch.py +164 -0
  69. datanyx-0.1.0/src/datanyx/text/custom.py +166 -0
  70. datanyx-0.1.0/src/datanyx/text/detectors.py +263 -0
  71. datanyx-0.1.0/src/datanyx/text/documents.py +464 -0
  72. datanyx-0.1.0/src/datanyx/text/mapping.py +79 -0
  73. datanyx-0.1.0/src/datanyx/text/packs.py +189 -0
  74. datanyx-0.1.0/src/datanyx/text/secrets.py +55 -0
  75. datanyx-0.1.0/src/datanyx.egg-info/PKG-INFO +146 -0
  76. datanyx-0.1.0/src/datanyx.egg-info/SOURCES.txt +109 -0
  77. datanyx-0.1.0/src/datanyx.egg-info/dependency_links.txt +1 -0
  78. datanyx-0.1.0/src/datanyx.egg-info/entry_points.txt +2 -0
  79. datanyx-0.1.0/src/datanyx.egg-info/requires.txt +64 -0
  80. datanyx-0.1.0/src/datanyx.egg-info/top_level.txt +1 -0
  81. datanyx-0.1.0/tests/test_audit.py +203 -0
  82. datanyx-0.1.0/tests/test_check.py +392 -0
  83. datanyx-0.1.0/tests/test_cli_process.py +89 -0
  84. datanyx-0.1.0/tests/test_config.py +209 -0
  85. datanyx-0.1.0/tests/test_custom_detectors.py +168 -0
  86. datanyx-0.1.0/tests/test_detection_quality.py +483 -0
  87. datanyx-0.1.0/tests/test_diff.py +236 -0
  88. datanyx-0.1.0/tests/test_docs.py +85 -0
  89. datanyx-0.1.0/tests/test_documents.py +589 -0
  90. datanyx-0.1.0/tests/test_file_sources.py +323 -0
  91. datanyx-0.1.0/tests/test_fuzz_leaks.py +129 -0
  92. datanyx-0.1.0/tests/test_keep_name.py +122 -0
  93. datanyx-0.1.0/tests/test_mcp_server.py +149 -0
  94. datanyx-0.1.0/tests/test_mongodb_server.py +59 -0
  95. datanyx-0.1.0/tests/test_more_sources.py +477 -0
  96. datanyx-0.1.0/tests/test_privacy.py +199 -0
  97. datanyx-0.1.0/tests/test_protect.py +553 -0
  98. datanyx-0.1.0/tests/test_pseudonymize.py +453 -0
  99. datanyx-0.1.0/tests/test_renderers.py +101 -0
  100. datanyx-0.1.0/tests/test_rich_schemas.py +324 -0
  101. datanyx-0.1.0/tests/test_scale_servers.py +51 -0
  102. datanyx-0.1.0/tests/test_scan.py +226 -0
  103. datanyx-0.1.0/tests/test_schema.py +197 -0
  104. datanyx-0.1.0/tests/test_scrub.py +227 -0
  105. datanyx-0.1.0/tests/test_shipping.py +76 -0
  106. datanyx-0.1.0/tests/test_sql_servers.py +396 -0
  107. datanyx-0.1.0/tests/test_sql_targets.py +88 -0
  108. datanyx-0.1.0/tests/test_sqlite.py +374 -0
  109. datanyx-0.1.0/tests/test_synth.py +251 -0
  110. datanyx-0.1.0/tests/test_text.py +422 -0
  111. datanyx-0.1.0/tests/test_text_model.py +65 -0
datanyx-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ludenberg
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.
datanyx-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,146 @@
1
+ Metadata-Version: 2.4
2
+ Name: datanyx
3
+ Version: 0.1.0
4
+ Summary: Turn sensitive data into a value-free schema that AI agents can work from.
5
+ License-Expression: MIT
6
+ Keywords: ai-agents,privacy,schema,anonymization,data
7
+ Classifier: Development Status :: 3 - Alpha
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.10
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Topic :: Security
16
+ Classifier: Topic :: Software Development :: Libraries
17
+ Requires-Python: >=3.10
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+ Requires-Dist: sqlalchemy>=2
21
+ Requires-Dist: tomli>=2; python_version < "3.11"
22
+ Provides-Extra: postgres
23
+ Requires-Dist: psycopg[binary]>=3.1; extra == "postgres"
24
+ Provides-Extra: mysql
25
+ Requires-Dist: pymysql>=1.1; extra == "mysql"
26
+ Requires-Dist: cryptography; extra == "mysql"
27
+ Provides-Extra: mssql
28
+ Requires-Dist: pymssql>=2.3; extra == "mssql"
29
+ Provides-Extra: oracle
30
+ Requires-Dist: oracledb>=2; extra == "oracle"
31
+ Provides-Extra: parquet
32
+ Requires-Dist: pyarrow>=14; extra == "parquet"
33
+ Provides-Extra: excel
34
+ Requires-Dist: openpyxl>=3.1; extra == "excel"
35
+ Provides-Extra: yaml
36
+ Requires-Dist: pyyaml>=6; extra == "yaml"
37
+ Provides-Extra: streaming
38
+ Requires-Dist: ijson>=3.2; extra == "streaming"
39
+ Provides-Extra: mcp
40
+ Requires-Dist: mcp>=1.2; extra == "mcp"
41
+ Provides-Extra: avro
42
+ Requires-Dist: fastavro>=1.9; extra == "avro"
43
+ Provides-Extra: orc
44
+ Requires-Dist: pyarrow>=14; extra == "orc"
45
+ Provides-Extra: duckdb
46
+ Requires-Dist: duckdb>=1.0; extra == "duckdb"
47
+ Provides-Extra: stats
48
+ Requires-Dist: pyreadstat>=1.2; (sys_platform != "win32" or python_version >= "3.11") and extra == "stats"
49
+ Provides-Extra: mongodb
50
+ Requires-Dist: pymongo>=4.6; extra == "mongodb"
51
+ Provides-Extra: all
52
+ Requires-Dist: datanyx[avro,check,duckdb,excel,mcp,mongodb,mssql,mysql,oracle,orc,parquet,pdf,postgres,presidio,stats,streaming,yaml]; extra == "all"
53
+ Provides-Extra: text
54
+ Requires-Dist: spacy>=3.7; extra == "text"
55
+ Provides-Extra: check
56
+ Requires-Dist: sqlglot>=25; extra == "check"
57
+ Provides-Extra: pdf
58
+ Requires-Dist: pypdf>=4; extra == "pdf"
59
+ Provides-Extra: presidio
60
+ Requires-Dist: presidio-analyzer>=2.2; extra == "presidio"
61
+ Dynamic: license-file
62
+
63
+ <div align="center">
64
+
65
+ # DataNyx
66
+
67
+ **Let an AI agent work with your data without ever seeing it.**
68
+
69
+ DataNyx turns files and databases into value-free schemas an agent can write code against,
70
+ and replaces names and identifiers in free text with placeholders.
71
+
72
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
73
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776ab.svg)](pyproject.toml)
74
+ [![Code style: ruff](https://img.shields.io/badge/lint-ruff-261230.svg)](https://docs.astral.sh/ruff/)
75
+ [![Types: mypy](https://img.shields.io/badge/types-mypy-2a6db2.svg)](https://mypy-lang.org/)
76
+
77
+ [Getting started](docs/getting-started.md) ·
78
+ [Documentation](docs/index.md) ·
79
+ [Command reference](docs/cli-reference.md) ·
80
+ [Security model](docs/security.md) ·
81
+ [Changelog](CHANGELOG.md)
82
+
83
+ </div>
84
+
85
+ ---
86
+
87
+ You want an agent to write a report, a query or a data-cleaning script, but the data is customers,
88
+ patients or payroll. Pasting rows into a chat is a leak, and describing the columns by hand is
89
+ tedious. DataNyx sits in between:
90
+
91
+ | | What the agent gets | What it never gets |
92
+ |---|---|---|
93
+ | **Structured data**<br>`datanyx schema` | Field names, types, formats, nesting, primary and foreign keys | Values, examples, enum labels, counts or statistics |
94
+ | **Free text**<br>`datanyx anonymize` | The text with `<PERSON_1>`, `<EMAIL_1>` and friends in place of identifiers | The identifiers themselves (best effort: [review the result](docs/anonymization.md#how-reliable-text-anonymization-is)) |
95
+
96
+ ## A first look
97
+
98
+ ```console
99
+ $ datanyx schema customers.csv --format text
100
+ root: array<{
101
+ id: integer
102
+ email: string<email>
103
+ signed_up: string<date>
104
+ plan: string
105
+ lifetime_value: number
106
+ notes: string | null
107
+ }>
108
+
109
+ $ datanyx anonymize call-notes.txt
110
+ <PERSON_1> (<EMAIL_1>) called on <DATE_1> about invoice
111
+ <ID_1>. Ms. <PERSON_1> lives in <LOCATION_1>. Her manager <PERSON_2> agreed.
112
+ ```
113
+
114
+ The agent sees the shape of the table and the story in the text, and none of the values. It can also
115
+ try its code on [fake rows](docs/synthetic-data.md) generated from the schema.
116
+
117
+ ## Install
118
+
119
+ ```sh
120
+ git clone <this repository> datanyx && cd datanyx
121
+ uv sync --all-extras # or: pip install -e ".[all]"
122
+ datanyx doctor # shows which optional packages and models are installed
123
+ ```
124
+
125
+ Python 3.10 or newer. [Supported sources](docs/sources.md) lists the extra for each format.
126
+
127
+ ## What's in the documentation
128
+
129
+ | | |
130
+ |---|---|
131
+ | [Getting started](docs/getting-started.md) | Install, a first schema, a first anonymized text |
132
+ | [Schemas](docs/schemas.md), [synthetic data](docs/synthetic-data.md) | The core: what the agent sees instead of the data |
133
+ | [Debugging](docs/debugging.md), [checking code](docs/checking-code.md) | Fixing code without seeing the data |
134
+ | [Scanning](docs/scanning.md), [anonymization](docs/anonymization.md), [pseudonymization](docs/pseudonymization.md), [detection quality](docs/detection.md) | Finding and hiding personal data |
135
+ | [Using it with an agent](docs/agents.md) | `datanyx protect`, the guard hook and the MCP server |
136
+ | [Installing and deploying](docs/deployment.md) | PyPI, Docker, a pre-commit hook, a GitHub Action, a shared MCP server |
137
+ | [Team settings](docs/settings.md), [schema diff](docs/schema-diff.md) | Shared policy, and change detection for CI |
138
+ | [Security model](docs/security.md) | What is guaranteed, what isn't, and the known limits |
139
+ | [Command reference](docs/cli-reference.md), [Python API](docs/python-api.md) | Every option, and use from code |
140
+ | [Contributing](docs/contributing.md) | Architecture, adding a source, tests |
141
+
142
+ To read it as a site: `uv sync --group docs` then `uv run mkdocs serve`.
143
+
144
+ ## License
145
+
146
+ [MIT](LICENSE) © 2026 ludenberg
@@ -0,0 +1,84 @@
1
+ <div align="center">
2
+
3
+ # DataNyx
4
+
5
+ **Let an AI agent work with your data without ever seeing it.**
6
+
7
+ DataNyx turns files and databases into value-free schemas an agent can write code against,
8
+ and replaces names and identifiers in free text with placeholders.
9
+
10
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
11
+ [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-3776ab.svg)](pyproject.toml)
12
+ [![Code style: ruff](https://img.shields.io/badge/lint-ruff-261230.svg)](https://docs.astral.sh/ruff/)
13
+ [![Types: mypy](https://img.shields.io/badge/types-mypy-2a6db2.svg)](https://mypy-lang.org/)
14
+
15
+ [Getting started](docs/getting-started.md) ·
16
+ [Documentation](docs/index.md) ·
17
+ [Command reference](docs/cli-reference.md) ·
18
+ [Security model](docs/security.md) ·
19
+ [Changelog](CHANGELOG.md)
20
+
21
+ </div>
22
+
23
+ ---
24
+
25
+ You want an agent to write a report, a query or a data-cleaning script, but the data is customers,
26
+ patients or payroll. Pasting rows into a chat is a leak, and describing the columns by hand is
27
+ tedious. DataNyx sits in between:
28
+
29
+ | | What the agent gets | What it never gets |
30
+ |---|---|---|
31
+ | **Structured data**<br>`datanyx schema` | Field names, types, formats, nesting, primary and foreign keys | Values, examples, enum labels, counts or statistics |
32
+ | **Free text**<br>`datanyx anonymize` | The text with `<PERSON_1>`, `<EMAIL_1>` and friends in place of identifiers | The identifiers themselves (best effort: [review the result](docs/anonymization.md#how-reliable-text-anonymization-is)) |
33
+
34
+ ## A first look
35
+
36
+ ```console
37
+ $ datanyx schema customers.csv --format text
38
+ root: array<{
39
+ id: integer
40
+ email: string<email>
41
+ signed_up: string<date>
42
+ plan: string
43
+ lifetime_value: number
44
+ notes: string | null
45
+ }>
46
+
47
+ $ datanyx anonymize call-notes.txt
48
+ <PERSON_1> (<EMAIL_1>) called on <DATE_1> about invoice
49
+ <ID_1>. Ms. <PERSON_1> lives in <LOCATION_1>. Her manager <PERSON_2> agreed.
50
+ ```
51
+
52
+ The agent sees the shape of the table and the story in the text, and none of the values. It can also
53
+ try its code on [fake rows](docs/synthetic-data.md) generated from the schema.
54
+
55
+ ## Install
56
+
57
+ ```sh
58
+ git clone <this repository> datanyx && cd datanyx
59
+ uv sync --all-extras # or: pip install -e ".[all]"
60
+ datanyx doctor # shows which optional packages and models are installed
61
+ ```
62
+
63
+ Python 3.10 or newer. [Supported sources](docs/sources.md) lists the extra for each format.
64
+
65
+ ## What's in the documentation
66
+
67
+ | | |
68
+ |---|---|
69
+ | [Getting started](docs/getting-started.md) | Install, a first schema, a first anonymized text |
70
+ | [Schemas](docs/schemas.md), [synthetic data](docs/synthetic-data.md) | The core: what the agent sees instead of the data |
71
+ | [Debugging](docs/debugging.md), [checking code](docs/checking-code.md) | Fixing code without seeing the data |
72
+ | [Scanning](docs/scanning.md), [anonymization](docs/anonymization.md), [pseudonymization](docs/pseudonymization.md), [detection quality](docs/detection.md) | Finding and hiding personal data |
73
+ | [Using it with an agent](docs/agents.md) | `datanyx protect`, the guard hook and the MCP server |
74
+ | [Installing and deploying](docs/deployment.md) | PyPI, Docker, a pre-commit hook, a GitHub Action, a shared MCP server |
75
+ | [Team settings](docs/settings.md), [schema diff](docs/schema-diff.md) | Shared policy, and change detection for CI |
76
+ | [Security model](docs/security.md) | What is guaranteed, what isn't, and the known limits |
77
+ | [Command reference](docs/cli-reference.md), [Python API](docs/python-api.md) | Every option, and use from code |
78
+ | [Contributing](docs/contributing.md) | Architecture, adding a source, tests |
79
+
80
+ To read it as a site: `uv sync --group docs` then `uv run mkdocs serve`.
81
+
82
+ ## License
83
+
84
+ [MIT](LICENSE) © 2026 ludenberg
@@ -0,0 +1,130 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "datanyx"
7
+ version = "0.1.0"
8
+ description = "Turn sensitive data into a value-free schema that AI agents can work from."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ keywords = ["ai-agents", "privacy", "schema", "anonymization", "data"]
14
+ classifiers = [
15
+ "Development Status :: 3 - Alpha",
16
+ "Environment :: Console",
17
+ "Intended Audience :: Developers",
18
+ "Programming Language :: Python :: 3",
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
+ "Topic :: Security",
24
+ "Topic :: Software Development :: Libraries",
25
+ ]
26
+ dependencies = ["sqlalchemy>=2", "tomli>=2; python_version < '3.11'"]
27
+
28
+ [project.optional-dependencies]
29
+ postgres = ["psycopg[binary]>=3.1"]
30
+ mysql = ["pymysql>=1.1", "cryptography"]
31
+ mssql = ["pymssql>=2.3"]
32
+ oracle = ["oracledb>=2"]
33
+ parquet = ["pyarrow>=14"]
34
+ excel = ["openpyxl>=3.1"]
35
+ yaml = ["pyyaml>=6"]
36
+ streaming = ["ijson>=3.2"]
37
+ mcp = ["mcp>=1.2"]
38
+ avro = ["fastavro>=1.9"]
39
+ orc = ["pyarrow>=14"]
40
+ duckdb = ["duckdb>=1.0"]
41
+ # pyreadstat publishes no Windows wheel for Python 3.10, and building it there needs a C library, so
42
+ # the extra is empty on that one platform and SAS, Stata and SPSS files report that they need it.
43
+ stats = ["pyreadstat>=1.2; sys_platform != 'win32' or python_version >= '3.11'"]
44
+ mongodb = ["pymongo>=4.6"]
45
+ all = ["datanyx[postgres,mysql,mssql,oracle,parquet,excel,yaml,streaming,mcp,check,pdf,presidio,avro,orc,duckdb,stats,mongodb]"]
46
+ text = ["spacy>=3.7"]
47
+ check = [
48
+ "sqlglot>=25",
49
+ ]
50
+ pdf = [
51
+ "pypdf>=4",
52
+ ]
53
+ presidio = [
54
+ "presidio-analyzer>=2.2",
55
+ ]
56
+
57
+ [dependency-groups]
58
+ dev = [
59
+ "pytest>=8",
60
+ "pytest-cov>=5",
61
+ "hypothesis>=6",
62
+ "ruff>=0.6",
63
+ "mypy>=1.10",
64
+ "types-PyYAML",
65
+ "pandas>=2.3.3",
66
+ "mongomock>=4.1",
67
+ ]
68
+ docs = [
69
+ "mkdocs>=1.6",
70
+ "mkdocs-material>=9.5",
71
+ ]
72
+ # Large NER models, installed from spaCy's releases. Needed only by the text tests.
73
+ models = [
74
+ "en-core-web-lg @ https://github.com/explosion/spacy-models/releases/download/en_core_web_lg-3.8.0/en_core_web_lg-3.8.0-py3-none-any.whl",
75
+ "fr-core-news-md @ https://github.com/explosion/spacy-models/releases/download/fr_core_news_md-3.8.0/fr_core_news_md-3.8.0-py3-none-any.whl",
76
+ ]
77
+
78
+ [project.scripts]
79
+ datanyx = "datanyx.cli:main"
80
+
81
+ [tool.setuptools.packages.find]
82
+ where = ["src"]
83
+
84
+ [tool.pytest.ini_options]
85
+ testpaths = ["tests"]
86
+ pythonpath = ["src"]
87
+ markers = [
88
+ "server: needs a real database server, started from tests/docker-compose.yml",
89
+ "needs_model: runs the default spaCy model, which only some CI jobs install",
90
+ ]
91
+
92
+ [tool.ruff]
93
+ line-length = 120
94
+ target-version = "py310"
95
+ src = ["src", "tests"]
96
+
97
+ [tool.ruff.lint]
98
+ select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF"]
99
+ ignore = [
100
+ "E501", "B904", "SIM105", "RUF012",
101
+ "B905", # zip() on rows of unequal length is intended: short rows leave columns out.
102
+ "RUF001", # Typographic apostrophes and dotless i are test data and word lists.
103
+ "SIM115", # Streams that must outlive a `with` block (stdin, SQLite handles).
104
+ ]
105
+
106
+ [tool.ruff.lint.per-file-ignores]
107
+ "tests/*" = ["B017", "B028", "SIM117"]
108
+
109
+ [tool.mypy]
110
+ python_version = "3.10"
111
+ files = ["src/datanyx"]
112
+ ignore_missing_imports = true
113
+ warn_unused_ignores = true
114
+ check_untyped_defs = true
115
+
116
+ [tool.coverage.run]
117
+ source = ["src/datanyx"]
118
+ branch = true
119
+
120
+ [tool.coverage.report]
121
+ show_missing = true
122
+ skip_covered = true
123
+
124
+ [[tool.mypy.overrides]]
125
+ module = "datanyx.mcp_server"
126
+ # Which of the two MCP library versions is installed decides which ignores are needed.
127
+ warn_unused_ignores = false
128
+
129
+ [tool.uv]
130
+ default-groups = ["dev"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,117 @@
1
+ """DataNyx: describe data to an AI agent without showing it the data."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from collections.abc import Iterable
7
+ from dataclasses import replace
8
+
9
+ from .keys import Evidence, apply_evidence
10
+ from .privacy import KeyPolicy
11
+ from .renderers import RENDERERS
12
+ from .schema import DataSchema
13
+ from .sources import Source, SourceError, describe_folder, resolve_source
14
+ from .text import DEFAULT_MODEL
15
+
16
+ __version__ = "0.1.0"
17
+
18
+
19
+ def extract(
20
+ target: str,
21
+ source: str | None = None,
22
+ policy: KeyPolicy | None = None,
23
+ namespace: str | None = None,
24
+ recursive: bool = False,
25
+ ) -> DataSchema:
26
+ """Build a value-free schema for a file path, "-" for stdin, a database URL or a folder.
27
+
28
+ `namespace` picks a database schema; the database's default is used otherwise.
29
+ For a folder, every readable file in it is described (and in its subfolders,
30
+ when `recursive` is set); `source` then limits which files are read.
31
+ """
32
+ policy = policy or KeyPolicy()
33
+ if policy.infer_keys and policy.evidence is None:
34
+ policy = replace(policy, evidence=Evidence())
35
+ if os.path.isdir(target):
36
+ if namespace is not None:
37
+ raise SourceError("a schema can only be selected for SQL databases")
38
+ schema = describe_folder(target, source, policy, recursive)
39
+ else:
40
+ schema = resolve_source(target, source).extract(target, policy, namespace)
41
+ if policy.infer_keys and policy.evidence is not None:
42
+ schema = apply_evidence(schema, policy.evidence)
43
+ if not any(e.foreign_keys or _has_unique(e) for e in schema.entities):
44
+ schema = replace(schema, notes=(*schema.notes, "no unique columns or joins were inferred; this kind of source may not support it, or no key-looking column qualified"))
45
+ if policy.scale_hints and not any(entity.scale for entity in schema.entities):
46
+ schema = replace(schema, notes=(*schema.notes, "no row-count hints: this kind of source can't give one without reading it"))
47
+ return schema
48
+
49
+
50
+ def _has_unique(entity) -> bool:
51
+ node = getattr(entity.node, "items", entity.node)
52
+ return any(field.unique for field in getattr(node, "fields", {}).values())
53
+
54
+
55
+ def render(schema: DataSchema, output_format: str = "json-schema") -> str:
56
+ """Render a schema in one of the formats listed in `RENDERERS`."""
57
+ return RENDERERS[output_format](schema)
58
+
59
+
60
+ def schema_of(
61
+ target: str,
62
+ output_format: str = "json-schema",
63
+ *,
64
+ source: str | None = None,
65
+ map_paths: Iterable[str] = (),
66
+ keep_names: Iterable[str] = (),
67
+ namespace: str | None = None,
68
+ recursive: bool = False,
69
+ ) -> str:
70
+ """The schema of `target` as text, in one call.
71
+
72
+ >>> print(datanyx.schema_of("customers.csv", "text"))
73
+
74
+ Raises `SourceError` with a message that never quotes the data. Use `extract` and
75
+ `render` instead to get the schema as objects, or to see its `notes`.
76
+ """
77
+ policy = KeyPolicy(map_paths=frozenset(map_paths), keep_names=frozenset(keep_names))
78
+ schema = extract(target, source, policy, namespace, recursive)
79
+ return render(schema, output_format)
80
+
81
+
82
+ def anonymize_text(
83
+ text: str,
84
+ *,
85
+ model: str = DEFAULT_MODEL,
86
+ hide: Iterable[str] = (),
87
+ keep: Iterable[str] = (),
88
+ keep_entity: Iterable[str] = (),
89
+ ) -> str:
90
+ """`text` with names and identifiers replaced by placeholders such as <PERSON_1>.
91
+
92
+ Needs `datanyx[text]` and a spaCy model. Best effort: review the result.
93
+ """
94
+ from .text import Detector, PatternDetector, SpacyDetector, anonymize
95
+
96
+ detectors: list[Detector] = [PatternDetector(), SpacyDetector(model)]
97
+ return anonymize(text, detectors, hide, keep, keep_entity)
98
+
99
+
100
+ def synthesize(schema: DataSchema, rows: int = 100, seed: int | None = 0):
101
+ """Fake data matching `schema`, as a list of `datanyx.synth.Table`. See `datanyx.synth`."""
102
+ from .synth import synthesize as generate
103
+
104
+ return generate(schema, rows, seed)
105
+
106
+
107
+ __all__ = [
108
+ "DataSchema",
109
+ "KeyPolicy",
110
+ "Source",
111
+ "SourceError",
112
+ "anonymize_text",
113
+ "extract",
114
+ "render",
115
+ "schema_of",
116
+ "synthesize",
117
+ ]
@@ -0,0 +1,5 @@
1
+ import sys
2
+
3
+ from .cli import main
4
+
5
+ sys.exit(main())