data-sampler 3.4.1__tar.gz → 3.5.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.
- {data_sampler-3.4.1 → data_sampler-3.5.0}/CHANGELOG.md +39 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/PKG-INFO +38 -4
- {data_sampler-3.4.1 → data_sampler-3.5.0}/README.md +37 -3
- data_sampler-3.5.0/docs/img/tui-columns.png +0 -0
- data_sampler-3.5.0/docs/img/tui-file.png +0 -0
- data_sampler-3.5.0/docs/img/tui-report.png +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/pyproject.toml +1 -1
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/__init__.py +26 -2
- data_sampler-3.5.0/src/data_sampler/_names.py +685 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/anonymize.py +246 -34
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/engine.py +50 -5
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/io.py +26 -6
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/tui/app.py +511 -22
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_anonymize.py +6 -5
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_engine.py +17 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_io.py +36 -1
- data_sampler-3.5.0/tests/test_names.py +194 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_tui.py +121 -1
- data_sampler-3.4.1/src/data_sampler/_names.py +0 -60
- {data_sampler-3.4.1 → data_sampler-3.5.0}/.gitattributes +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/.github/workflows/release.yml +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/.gitignore +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/LICENSE +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/ROADMAP.md +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0/docs}/RELEASING.md +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0/docs}/TROUBLESHOOTING.md +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/examples/employees.csv +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/examples/using_data_sampler.ipynb +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/requirements.txt +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/scripts/run-tui.bat +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/scripts/run-tui.sh +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/__main__.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/_logging.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/cli.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/reduce.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/report.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/sampling.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/stats.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/tui/__init__.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/src/data_sampler/workflow.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/conftest.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_cli.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_reduce.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_report.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_sampling.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_stats.py +0 -0
- {data_sampler-3.4.1 → data_sampler-3.5.0}/tests/test_workflow.py +0 -0
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v3.5.0 — 2026-07-24
|
|
4
|
+
|
|
5
|
+
- **Names anonymizer is now gender- and ethnicity-aware.** The bundled library
|
|
6
|
+
(`_names.py`) is regrouped into real, hand-checked names keyed by
|
|
7
|
+
`<ethnicity>_<gender>` across **33 ethnic groups** (multiple Indian
|
|
8
|
+
regions/cultures, Chinese, Japanese, Korean, Vietnamese, Anglo, Italian,
|
|
9
|
+
French, German, Russian, Polish, Scandinavian, Greek, Turkish, Persian,
|
|
10
|
+
Arab-Levantine, Sudanese, Algerian, Moroccan, Ethiopian, Rwandan, Yoruba,
|
|
11
|
+
Igbo, Ghanaian-Akan, Hispanic, Brazilian, Filipino, Indonesian). Surnames are
|
|
12
|
+
grouped too — gendered where real (Russian Ivanov/Ivanova, Polish -ski/-ska)
|
|
13
|
+
and with distinctly-female additions (North-Indian Devi/Kumari) over unisex
|
|
14
|
+
bases (Kumar/Das). `NameAnonymizer(gender=…, ethnicity=…)` fixes a gender
|
|
15
|
+
(`male`/`female`/`third`/`undisclosed`) and/or an ethnic group; **third
|
|
16
|
+
gender** mixes given names within an ethnicity, **undisclosed** draws from any
|
|
17
|
+
ethnicity.
|
|
18
|
+
- **Gender/ethnicity can be read from another column** and mapped:
|
|
19
|
+
`NameAnonymizer(gender_column=…, ethnicity_column=…)` with per-value
|
|
20
|
+
`gender_map` / `ethnicity_map` overrides. Values in any encoding (M/F, 0/1 per
|
|
21
|
+
ISO-5218, `male`/`female`, other languages) are auto-detected
|
|
22
|
+
(`suggest_gender_mapping` / `suggest_ethnicity_mapping`) with a manual
|
|
23
|
+
fallback. `randomize_gender=True` reassigns genders and rewrites the gender
|
|
24
|
+
column so both fields are anonymized together.
|
|
25
|
+
- **Custom name libraries.** `export_names_library()` writes the current library
|
|
26
|
+
as an editable module; `load_names_library(path=…)` (or the
|
|
27
|
+
`DATA_SAMPLER_NAMES` env var) activates one for the session; and
|
|
28
|
+
`install_names_library(path=…)` installs one permanently into the package.
|
|
29
|
+
- **Report screen shows a "reproduce this in Python" snippet** — the exact
|
|
30
|
+
`ds.load_file / sample / anonymize / reduce_columns / save_output` calls that
|
|
31
|
+
reproduce the run — and it's included in the saved report `.txt`. Every TUI
|
|
32
|
+
capability now maps to the public API.
|
|
33
|
+
- **Remote inputs.** `load_file` (and the TUI file screen) accept `http(s)` URLs
|
|
34
|
+
(e.g. GitHub raw links). The DuckDB engine loads `httpfs` on demand so it can
|
|
35
|
+
sample remote `s3://` / `https://` **Parquet with HTTP range requests** — a
|
|
36
|
+
multi-GB remote file is sampled out-of-core without downloading it whole.
|
|
37
|
+
- **TUI restyle** — btop-inspired rounded, quiet-until-focused inputs / selects
|
|
38
|
+
/ switches and outlined (non-solid) buttons; the columns table's per-stat
|
|
39
|
+
`mean`/`median`/`mode`/`sd` columns; the report save clarifies it includes the
|
|
40
|
+
histograms.
|
|
41
|
+
|
|
3
42
|
## v3.4.1 — 2026-07-24
|
|
4
43
|
|
|
5
44
|
- **TUI columns table now breaks the single summary cell into separate
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: data-sampler
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.5.0
|
|
4
4
|
Summary: Hand someone data that looks and behaves like your production data, isn't your production data, and provably kept its statistical variety — stratified sampling + consistent anonymization, with a terminal UI.
|
|
5
5
|
Project-URL: Homepage, https://github.com/aaronified/data-sampler
|
|
6
6
|
Project-URL: Changelog, https://github.com/aaronified/data-sampler/blob/main/CHANGELOG.md
|
|
@@ -75,6 +75,10 @@ Use it to:
|
|
|
75
75
|
trigger it.
|
|
76
76
|
- **Cut a 2 GB export down to 500 rows** for a demo or prototype that still
|
|
77
77
|
behaves like the real thing.
|
|
78
|
+
- **Narrow a wide table, not just shorten it** — optionally collapse the
|
|
79
|
+
numeric columns into a handful of principal components (PCA) on the way out,
|
|
80
|
+
so the shared sample is short *and* narrow, with a report of how much
|
|
81
|
+
variance each component keeps and which columns moved together.
|
|
78
82
|
- **Build test fixtures that mirror production skew** instead of uniform toy
|
|
79
83
|
data.
|
|
80
84
|
- **Give a class or workshop realistic data** without a data-sharing
|
|
@@ -103,6 +107,20 @@ Requires Python 3.10+. For development, clone the repo and
|
|
|
103
107
|
|
|
104
108
|
## Terminal UI
|
|
105
109
|
|
|
110
|
+
*Columns screen — per-column stats (mean / median / mode / sd), anonymizer,
|
|
111
|
+
stratify and reduce config, with multi-select for bulk edits:*
|
|
112
|
+
|
|
113
|
+

|
|
114
|
+
|
|
115
|
+
*File screen — type a path or pick one from the directory browser:*
|
|
116
|
+
|
|
117
|
+

|
|
118
|
+
|
|
119
|
+
*Report screen — stratification report on the left, source-vs-sample column
|
|
120
|
+
histograms on the right:*
|
|
121
|
+
|
|
122
|
+

|
|
123
|
+
|
|
106
124
|
```sh
|
|
107
125
|
data-sampler # no arguments → opens the TUI
|
|
108
126
|
data-sampler-tui # explicit TUI entry point
|
|
@@ -202,6 +220,9 @@ data-sampler wide.csv 500 --reduce-variance 0.9
|
|
|
202
220
|
import data_sampler as ds
|
|
203
221
|
|
|
204
222
|
df = ds.load_file("data.xlsx", sheet="Sheet2")
|
|
223
|
+
# …or straight from a URL (GitHub raw, S3, etc.); large remote Parquet can be
|
|
224
|
+
# sampled out-of-core with the DuckDB engine, without downloading it whole:
|
|
225
|
+
# df = ds.load_file("https://raw.githubusercontent.com/owner/repo/main/data.csv")
|
|
205
226
|
|
|
206
227
|
# Data Wrangler-style column stats
|
|
207
228
|
for s in ds.compute_stats(df):
|
|
@@ -226,6 +247,18 @@ anon = ds.anonymize(
|
|
|
226
247
|
seed=7,
|
|
227
248
|
)
|
|
228
249
|
|
|
250
|
+
# gender- and ethnicity-aware names: fix them, or read them from other columns
|
|
251
|
+
gendered = ds.anonymize(result.data, {
|
|
252
|
+
"name": ds.NameAnonymizer(gender="female", ethnicity="chinese"),
|
|
253
|
+
# or map per row from existing columns (values auto-detected + overridable):
|
|
254
|
+
# "name": ds.NameAnonymizer(gender_column="sex", ethnicity_column="origin"),
|
|
255
|
+
}, seed=7)
|
|
256
|
+
|
|
257
|
+
# bring your own names: export the library, edit it, load it back
|
|
258
|
+
ds.export_names_library("my_names.py") # editable copy of the current library
|
|
259
|
+
ds.load_names_library(path="my_names.py") # activate it for this session
|
|
260
|
+
# ds.install_names_library("my_names.py") # …or install permanently
|
|
261
|
+
|
|
229
262
|
# optionally collapse the numeric columns into principal components
|
|
230
263
|
red = ds.reduce_columns(anon, variance_ratio=0.9, exclude=["cust_id"])
|
|
231
264
|
print(ds.format_reduction_report(red)) # variance kept + correlated groups
|
|
@@ -344,7 +377,7 @@ Missing values are left as missing. All anonymizers accept a seed (via
|
|
|
344
377
|
|
|
345
378
|
| Kind | Replaces values with | Options (defaults) |
|
|
346
379
|
| --- | --- | --- |
|
|
347
|
-
| `names` | Realistic names from a bundled library
|
|
380
|
+
| `names` | Realistic names from a bundled library grouped by ethnicity + gender (33 groups) | `style`: `first_last`, `first_middle_last`, `last_first`, `first`, `last`; `gender`: `male`/`female`/`third`/`undisclosed`; `ethnicity`; or `gender_column`/`ethnicity_column` (+ `gender_map`/`ethnicity_map`, `randomize_gender`) |
|
|
348
381
|
| `sequential_id` | `start`, `start+interval`, ... in order of first appearance | `start` (1), `interval` (1), `prefix` (`""`), `width` (0, zero-pads) |
|
|
349
382
|
| `numeric_jitter` | A random number within ±`pct` of the original | `pct` (0.2 = ±20 %), `round_to` (decimal places) |
|
|
350
383
|
| `datetime_jitter` | A date/time shifted by a random offset within ±`max_delta` | `max_delta` (`"7D"`; any `pandas.Timedelta` string), `unit` (`"s"`; jitter resolution) |
|
|
@@ -592,10 +625,11 @@ python -m build # build the wheel + sdist into dist/
|
|
|
592
625
|
|
|
593
626
|
Logging is controlled by `DATA_SAMPLER_LOG` (`quiet`/`info`/`verbose`) and
|
|
594
627
|
`DATA_SAMPLER_LOG_FILE`. See `ROADMAP.md` for planned work and
|
|
595
|
-
`TROUBLESHOOTING.md` for known failure modes.
|
|
628
|
+
[`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md) for known failure modes.
|
|
596
629
|
|
|
597
630
|
Releases go to [PyPI](https://pypi.org/project/data-sampler/) via a
|
|
598
|
-
human-triggered, test-gated GitHub Actions workflow — see
|
|
631
|
+
human-triggered, test-gated GitHub Actions workflow — see
|
|
632
|
+
[`docs/RELEASING.md`](docs/RELEASING.md).
|
|
599
633
|
|
|
600
634
|
---
|
|
601
635
|
|
|
@@ -20,6 +20,10 @@ Use it to:
|
|
|
20
20
|
trigger it.
|
|
21
21
|
- **Cut a 2 GB export down to 500 rows** for a demo or prototype that still
|
|
22
22
|
behaves like the real thing.
|
|
23
|
+
- **Narrow a wide table, not just shorten it** — optionally collapse the
|
|
24
|
+
numeric columns into a handful of principal components (PCA) on the way out,
|
|
25
|
+
so the shared sample is short *and* narrow, with a report of how much
|
|
26
|
+
variance each component keeps and which columns moved together.
|
|
23
27
|
- **Build test fixtures that mirror production skew** instead of uniform toy
|
|
24
28
|
data.
|
|
25
29
|
- **Give a class or workshop realistic data** without a data-sharing
|
|
@@ -48,6 +52,20 @@ Requires Python 3.10+. For development, clone the repo and
|
|
|
48
52
|
|
|
49
53
|
## Terminal UI
|
|
50
54
|
|
|
55
|
+
*Columns screen — per-column stats (mean / median / mode / sd), anonymizer,
|
|
56
|
+
stratify and reduce config, with multi-select for bulk edits:*
|
|
57
|
+
|
|
58
|
+

|
|
59
|
+
|
|
60
|
+
*File screen — type a path or pick one from the directory browser:*
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
*Report screen — stratification report on the left, source-vs-sample column
|
|
65
|
+
histograms on the right:*
|
|
66
|
+
|
|
67
|
+

|
|
68
|
+
|
|
51
69
|
```sh
|
|
52
70
|
data-sampler # no arguments → opens the TUI
|
|
53
71
|
data-sampler-tui # explicit TUI entry point
|
|
@@ -147,6 +165,9 @@ data-sampler wide.csv 500 --reduce-variance 0.9
|
|
|
147
165
|
import data_sampler as ds
|
|
148
166
|
|
|
149
167
|
df = ds.load_file("data.xlsx", sheet="Sheet2")
|
|
168
|
+
# …or straight from a URL (GitHub raw, S3, etc.); large remote Parquet can be
|
|
169
|
+
# sampled out-of-core with the DuckDB engine, without downloading it whole:
|
|
170
|
+
# df = ds.load_file("https://raw.githubusercontent.com/owner/repo/main/data.csv")
|
|
150
171
|
|
|
151
172
|
# Data Wrangler-style column stats
|
|
152
173
|
for s in ds.compute_stats(df):
|
|
@@ -171,6 +192,18 @@ anon = ds.anonymize(
|
|
|
171
192
|
seed=7,
|
|
172
193
|
)
|
|
173
194
|
|
|
195
|
+
# gender- and ethnicity-aware names: fix them, or read them from other columns
|
|
196
|
+
gendered = ds.anonymize(result.data, {
|
|
197
|
+
"name": ds.NameAnonymizer(gender="female", ethnicity="chinese"),
|
|
198
|
+
# or map per row from existing columns (values auto-detected + overridable):
|
|
199
|
+
# "name": ds.NameAnonymizer(gender_column="sex", ethnicity_column="origin"),
|
|
200
|
+
}, seed=7)
|
|
201
|
+
|
|
202
|
+
# bring your own names: export the library, edit it, load it back
|
|
203
|
+
ds.export_names_library("my_names.py") # editable copy of the current library
|
|
204
|
+
ds.load_names_library(path="my_names.py") # activate it for this session
|
|
205
|
+
# ds.install_names_library("my_names.py") # …or install permanently
|
|
206
|
+
|
|
174
207
|
# optionally collapse the numeric columns into principal components
|
|
175
208
|
red = ds.reduce_columns(anon, variance_ratio=0.9, exclude=["cust_id"])
|
|
176
209
|
print(ds.format_reduction_report(red)) # variance kept + correlated groups
|
|
@@ -289,7 +322,7 @@ Missing values are left as missing. All anonymizers accept a seed (via
|
|
|
289
322
|
|
|
290
323
|
| Kind | Replaces values with | Options (defaults) |
|
|
291
324
|
| --- | --- | --- |
|
|
292
|
-
| `names` | Realistic names from a bundled library
|
|
325
|
+
| `names` | Realistic names from a bundled library grouped by ethnicity + gender (33 groups) | `style`: `first_last`, `first_middle_last`, `last_first`, `first`, `last`; `gender`: `male`/`female`/`third`/`undisclosed`; `ethnicity`; or `gender_column`/`ethnicity_column` (+ `gender_map`/`ethnicity_map`, `randomize_gender`) |
|
|
293
326
|
| `sequential_id` | `start`, `start+interval`, ... in order of first appearance | `start` (1), `interval` (1), `prefix` (`""`), `width` (0, zero-pads) |
|
|
294
327
|
| `numeric_jitter` | A random number within ±`pct` of the original | `pct` (0.2 = ±20 %), `round_to` (decimal places) |
|
|
295
328
|
| `datetime_jitter` | A date/time shifted by a random offset within ±`max_delta` | `max_delta` (`"7D"`; any `pandas.Timedelta` string), `unit` (`"s"`; jitter resolution) |
|
|
@@ -537,10 +570,11 @@ python -m build # build the wheel + sdist into dist/
|
|
|
537
570
|
|
|
538
571
|
Logging is controlled by `DATA_SAMPLER_LOG` (`quiet`/`info`/`verbose`) and
|
|
539
572
|
`DATA_SAMPLER_LOG_FILE`. See `ROADMAP.md` for planned work and
|
|
540
|
-
`TROUBLESHOOTING.md` for known failure modes.
|
|
573
|
+
[`docs/TROUBLESHOOTING.md`](docs/TROUBLESHOOTING.md) for known failure modes.
|
|
541
574
|
|
|
542
575
|
Releases go to [PyPI](https://pypi.org/project/data-sampler/) via a
|
|
543
|
-
human-triggered, test-gated GitHub Actions workflow — see
|
|
576
|
+
human-triggered, test-gated GitHub Actions workflow — see
|
|
577
|
+
[`docs/RELEASING.md`](docs/RELEASING.md).
|
|
544
578
|
|
|
545
579
|
---
|
|
546
580
|
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "data-sampler"
|
|
7
|
-
version = "3.
|
|
7
|
+
version = "3.5.0"
|
|
8
8
|
description = "Hand someone data that looks and behaves like your production data, isn't your production data, and provably kept its statistical variety — stratified sampling + consistent anonymization, with a terminal UI."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -18,7 +18,7 @@ Public API::
|
|
|
18
18
|
|
|
19
19
|
from __future__ import annotations
|
|
20
20
|
|
|
21
|
-
__version__ = "3.
|
|
21
|
+
__version__ = "3.5.0"
|
|
22
22
|
|
|
23
23
|
from .io import SUPPORTED_EXTENSIONS, list_sheets, load_file, save_output
|
|
24
24
|
from .reduce import ReductionResult, reduce_columns
|
|
@@ -60,9 +60,16 @@ __all__ = [
|
|
|
60
60
|
"format_column_histograms",
|
|
61
61
|
"anonymize",
|
|
62
62
|
"make_anonymizer",
|
|
63
|
+
"NameAnonymizer",
|
|
64
|
+
"suggest_gender_mapping",
|
|
65
|
+
"suggest_ethnicity_mapping",
|
|
63
66
|
"AnonymizationPlan",
|
|
64
67
|
"suggest_type",
|
|
65
68
|
"TYPE_OPTIONS",
|
|
69
|
+
"export_names_library",
|
|
70
|
+
"load_names_library",
|
|
71
|
+
"install_names_library",
|
|
72
|
+
"name_ethnicities",
|
|
66
73
|
"run_tui",
|
|
67
74
|
]
|
|
68
75
|
|
|
@@ -70,13 +77,30 @@ __all__ = [
|
|
|
70
77
|
def __getattr__(name: str):
|
|
71
78
|
# Lazy imports: keep pandas-only workflows fast and let the core API work
|
|
72
79
|
# even while optional pieces (TUI) are not yet importable.
|
|
73
|
-
if name in (
|
|
80
|
+
if name in (
|
|
81
|
+
"anonymize", "make_anonymizer", "NameAnonymizer",
|
|
82
|
+
"suggest_gender_mapping", "suggest_ethnicity_mapping",
|
|
83
|
+
):
|
|
74
84
|
# importlib, not `from . import anonymize`: the fromlist form calls
|
|
75
85
|
# hasattr(package, "anonymize"), which re-enters this __getattr__.
|
|
76
86
|
import importlib
|
|
77
87
|
|
|
78
88
|
_anon = importlib.import_module(".anonymize", __name__)
|
|
79
89
|
return getattr(_anon, name)
|
|
90
|
+
if name in (
|
|
91
|
+
"export_names_library", "load_names_library",
|
|
92
|
+
"install_names_library", "name_ethnicities",
|
|
93
|
+
):
|
|
94
|
+
import importlib
|
|
95
|
+
|
|
96
|
+
_n = importlib.import_module("._names", __name__)
|
|
97
|
+
_alias = {
|
|
98
|
+
"export_names_library": "export_library",
|
|
99
|
+
"load_names_library": "load_library",
|
|
100
|
+
"install_names_library": "install_library",
|
|
101
|
+
"name_ethnicities": "ETHNICITIES",
|
|
102
|
+
}[name]
|
|
103
|
+
return getattr(_n, _alias)
|
|
80
104
|
if name in ("AnonymizationPlan", "suggest_type", "TYPE_OPTIONS"):
|
|
81
105
|
import importlib
|
|
82
106
|
|