tdfs4ds 0.2.4.32__py3-none-any.whl → 0.2.4.34__py3-none-any.whl

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.
@@ -2,7 +2,7 @@ import datetime
2
2
  import numpy as np # Needed for np.datetime64 handling in get_date_in_the_past
3
3
  import teradataml as tdml
4
4
  import tdfs4ds
5
- from tdfs4ds import logger
5
+ from tdfs4ds import logger, logger_safe
6
6
 
7
7
 
8
8
  def get_hidden_table_name(table_name):
@@ -20,24 +20,60 @@ def get_hidden_table_name(table_name):
20
20
 
21
21
  class FilterManager:
22
22
  """
23
- Manage dynamic, filter-driven views over a Teradata table.
24
-
25
- This class maintains a hidden table that stores one or more *filters* (one row
26
- per filter definition) and exposes a public view whose SELECT projects the
27
- columns from the hidden table for the currently-active filter ID. You can load
28
- new filters, switch which filter the view points at, prune older filters, and
29
- (optionally) include a time dimension via a `BUSINESS_DATE` column.
23
+ A utility for managing dynamic, versioned filter sets as database-backed views.
24
+
25
+ The FilterManager enables lightweight scenario management by storing multiple
26
+ filter definitions in a hidden Teradata table and exposing a public view that
27
+ dynamically switches between them by `filter_id`. Each row in the hidden table
28
+ represents a complete filter configuration. The active configuration is
29
+ controlled by updating the view definition rather than rewriting table data.
30
+
31
+ Key Features:
32
+ - Store multiple filter states (scenarios) indexed by `filter_id`
33
+ - Switch filter states instantly by updating a view
34
+ - Optionally include time-based slicing using a `BUSINESS_DATE` column
35
+ - Clone filters between managers (soft or hard clone modes)
36
+ - Prune obsolete filters to control table size
37
+ - Retrieve current and historical filter definitions
38
+
39
+ Workflow Overview:
40
+ 1. Create a `FilterManager` pointing to a target view name.
41
+ 2. Load one or more filter definitions using `load_filter()`.
42
+ 3. Switch active filters using `update(filter_id)`.
43
+ 4. Inspect the active filter via `display()` or view DDL.
44
+ 5. Optionally prune or clone filters as needed.
45
+
46
+ How It Works Internally:
47
+ - A hidden table named `<view_name>_HIDDEN` stores filter definitions.
48
+ - A Teradata view named `<view_name>` exposes only the *active* filter row.
49
+ - Each filter automatically receives a sequential `filter_id`
50
+ (`ROW_NUMBER()` ordering ensures deterministic assignment).
51
+ - If time-based filtering is used via `time_column`, a `BUSINESS_DATE`
52
+ column is added and projected in all operations.
53
+
54
+ Parameters:
55
+ table_name (str): Public view name to manage or create.
56
+ schema_name (str): Teradata schema where artifacts will be created.
57
+ filter_id_name (str, optional): Name of the filter ID column. Defaults to `'filter_id'`.
58
+ time_column (str, optional): Optional name of a timestamp column from input DataFrames
59
+ that maps to a `BUSINESS_DATE` column for time-aware filters.
30
60
 
31
61
  Attributes:
32
- schema_name (str): Database schema that contains the hidden table and public view.
33
- table_name (str): Hidden table name where filter rows are stored.
34
- view_name (str): Public view name pointing at the currently active filter row.
35
- filter_id_name (str): Column name containing the filter identifier (default 'filter_id').
36
- nb_filters (int | None): Number of filters currently stored (max filter_id). Populated when objects exist.
37
- col_names (list[str] | None): Columns projected by the view (excludes filter-id and time column).
38
- time_filtering (bool | None): Whether a `BUSINESS_DATE` column is present/used.
62
+ schema_name (str): Target schema for view and hidden table.
63
+ table_name (str): Name of hidden table storing filters (auto-suffixed with `_HIDDEN`).
64
+ view_name (str): Name of public view pointing to current filter.
65
+ filter_id_name (str): Column containing filter ID.
66
+ nb_filters (int | None): Number of stored filters (None until initialized).
67
+ col_names (list[str] | None): Columns projected by the view (data columns only).
68
+ time_filtering (bool | None): True if time-based filtering enabled.
69
+
70
+ Notes:
71
+ - Database objects are only created when `load_filter()` is first called.
72
+ - Safe for iterative pipeline runs—auto-detects existing artifacts.
73
+ - Designed for large production tables and Teradata-native workflows.
39
74
  """
40
75
 
76
+
41
77
  def __init__(self, table_name, schema_name, filter_id_name="filter_id", time_column=None):
42
78
  """
43
79
  Initialize the FilterManager.
@@ -46,15 +82,6 @@ class FilterManager:
46
82
  filter id, and time filtering status) are detected and cached. If they do
47
83
  not exist yet, attributes are initialized but no objects are created until
48
84
  `load_filter()` is called.
49
-
50
- Args:
51
- table_name (str): Public view name to maintain.
52
- schema_name (str): Schema where the view and hidden table live.
53
- filter_id_name (str, optional): Name of the filter id column. Defaults to 'filter_id'.
54
- time_column (str, optional): If provided, indicates the source column
55
- in incoming DataFrames to copy into `BUSINESS_DATE` during `load_filter()`.
56
- (Note: this parameter is remembered but the actual `BUSINESS_DATE`
57
- column is only created/used when `load_filter(time_column=...)` is called.)
58
85
  """
59
86
  self.schema_name = schema_name
60
87
  self.table_name = get_hidden_table_name(table_name)
@@ -63,46 +90,45 @@ class FilterManager:
63
90
  self.nb_filters = None
64
91
  self.col_names = None
65
92
  self.time_filtering = None
66
- self._init_time_column = time_column # remember user hint for later `load_filter` calls
93
+ self._init_time_column = time_column # Remember user hint for later
67
94
 
68
- logger.debug(
69
- "Initializing FilterManager",
70
- extra={
71
- "schema_name": self.schema_name,
72
- "view_name": self.view_name,
73
- "table_name": self.table_name,
74
- "filter_id_name": self.filter_id_name,
75
- },
95
+ logger_safe(
96
+ "debug",
97
+ "Initializing FilterManager | schema_name=%s | view_name=%s | table_name=%s | filter_id_name=%s",
98
+ self.schema_name, self.view_name, self.table_name, self.filter_id_name
76
99
  )
77
100
 
78
101
  if self._exists():
79
- logger.info(
80
- "Existing filter artifacts detected.",
81
- extra={"schema_name": self.schema_name, "view_name": self.view_name, "table_name": self.table_name},
102
+ logger_safe(
103
+ "info",
104
+ "Existing filter artifacts detected | schema_name=%s | view_name=%s | table_name=%s",
105
+ self.schema_name, self.view_name, self.table_name
82
106
  )
107
+
83
108
  df = tdml.DataFrame(tdml.in_schema(self.schema_name, self.table_name))
84
- # First column is assumed to be the filter id
85
- self.filter_id_name = df.columns[0]
109
+ self.filter_id_name = df.columns[0] # First column is assumed to be filter id
110
+
86
111
  self.nb_filters = tdml.execute_sql(
87
112
  f"SEL MAX({self.filter_id_name}) AS nb_filters FROM {self.schema_name}.{self.table_name}"
88
113
  ).fetchall()[0][0]
114
+
89
115
  self.time_filtering = self._istimefiltering()
90
116
  self.col_names = df.columns[2:] if self.time_filtering else df.columns[1:]
91
- logger.debug(
92
- "Detected existing configuration.",
93
- extra={
94
- "filter_id_name": self.filter_id_name,
95
- "nb_filters": self.nb_filters,
96
- "time_filtering": self.time_filtering,
97
- "col_names": list(self.col_names),
98
- },
117
+
118
+ logger_safe(
119
+ "debug",
120
+ "Detected existing configuration | filter_id_name=%s | nb_filters=%s | time_filtering=%s | col_names=%s",
121
+ self.filter_id_name, self.nb_filters, self.time_filtering, list(self.col_names)
99
122
  )
123
+
100
124
  else:
101
- logger.info(
102
- "No existing filter artifacts found; will be created by load_filter().",
103
- extra={"schema_name": self.schema_name, "view_name": self.view_name, "table_name": self.table_name},
125
+ logger_safe(
126
+ "info",
127
+ "No existing filter artifacts found; will be created by load_filter() | schema_name=%s | view_name=%s",
128
+ self.schema_name, self.view_name
104
129
  )
105
130
 
131
+
106
132
  def _istimefiltering(self):
107
133
  """
108
134
  Determine if the hidden table includes a `BUSINESS_DATE` column.
@@ -148,7 +174,7 @@ class FilterManager:
148
174
  Raises:
149
175
  ValueError: If `time_column` is provided but not present in `df`.
150
176
  """
151
- logger.info("Loading filters", extra={"rows": len(df), "time_column": time_column})
177
+ logger.info("Loading filters", extra={"rows": df.shape[0], "time_column": time_column})
152
178
 
153
179
  if time_column and time_column not in df.columns:
154
180
  logger.error("Specified time_column not found in DataFrame.", extra={"time_column": time_column})
@@ -263,15 +289,17 @@ class FilterManager:
263
289
  Raises:
264
290
  ValueError: If filter artifacts do not exist yet.
265
291
  """
266
- logger.info("Updating active filter", extra={"filter_id": filter_id})
292
+
267
293
 
268
294
  if not self._exists():
269
- logger.error("Filter artifacts not initialized.")
295
+ logger_safe("error", "Filter artifacts not initialized.")
270
296
  raise ValueError("The filter has not been initialized with load_filter() or has been deleted.")
271
297
 
272
298
  if self.time_filtering:
299
+ select_cols_str = ["BUSINESS_DATE"] + list(self.col_names)
273
300
  select_cols = ",".join(["BUSINESS_DATE"] + list(self.col_names))
274
301
  else:
302
+ select_cols_str = list(self.col_names)
275
303
  select_cols = ",".join(self.col_names)
276
304
 
277
305
  query = f"""
@@ -280,8 +308,14 @@ class FilterManager:
280
308
  FROM {self.schema_name}.{self.table_name}
281
309
  WHERE {self.filter_id_name} = {filter_id}
282
310
  """
283
- logger.debug("Replacing view with new filter", extra={"query": query})
311
+ logger_safe("info", "Updating active filter | %s", ','.join([f"{c}:{v}" for c,v in zip(select_cols_str, tdml.execute_sql(f"SEL * FROM {self.schema_name}.{self.view_name}").fetchall()[0])]))
312
+
313
+ if getattr(tdfs4ds, "DEBUG_MODE", False):
314
+ logger_safe("debug", "Replacing view with new filter:\n%s", query)
315
+
284
316
  tdml.execute_sql(query)
317
+ logger_safe("debug", "View %s.%s updated to filter_id=%s", self.schema_name, self.view_name, filter_id)
318
+
285
319
 
286
320
  def display(self):
287
321
  """
@@ -1,6 +1,6 @@
1
1
  import teradataml as tdml
2
2
  import datetime
3
- from tdfs4ds import logger
3
+ from tdfs4ds import logger, logger_safe
4
4
  import re
5
5
 
6
6
  import tdfs4ds
@@ -22,23 +22,62 @@ def get_hidden_table_name(table_name: str) -> str:
22
22
 
23
23
  class TimeManager:
24
24
  """
25
- Manage time-step metadata for a Teradata table and a companion view.
26
-
27
- This class maintains a hidden physical table (``<view>_HIDDEN``) that stores
28
- sequential time steps (``TIME_ID``) mapped to a business date/time
29
- (``BUSINESS_DATE``). It also maintains a public view (``<view>``) that
30
- exposes the *current* business date/time by filtering the hidden table on
31
- a specific ``TIME_ID``.
32
-
33
- Attributes:
34
- schema_name: Target database/schema.
35
- table_name: Hidden physical table name (``<view>_HIDDEN``).
36
- view_name: Public view name (``<view>``).
37
- time_id: Name of the incrementing identifier column (default: ``"time_id"``).
38
- nb_time_steps: Number of time steps currently stored in the hidden table
39
- (``None`` if the hidden table does not exist yet).
40
- data_type: SQL data type of ``BUSINESS_DATE`` (e.g., ``DATE``,
41
- ``TIMESTAMP WITH TIME ZONE``), set after load/inspection.
25
+ Manage versioned business time steps behind a Teradata-backed public view.
26
+
27
+ `TimeManager` stores a sequence of time “snapshots” in a hidden physical
28
+ table (`<view_name>_HIDDEN`) with two columns:
29
+ - `TIME_ID` (1..N): the step index, assigned deterministically via
30
+ `ROW_NUMBER()` over the input time column.
31
+ - `BUSINESS_DATE`: the business date/time associated with each step. When
32
+ loading, the SQL type is inferred and upcast to `TIMESTAMP WITH TIME ZONE`
33
+ when needed to preserve offsets.
34
+
35
+ A companion public view (`<view_name>`) always exposes the *current* business
36
+ date by filtering the hidden table on a single `TIME_ID`. Changing the
37
+ current step only rewrites the view definition—no data is mutated.
38
+
39
+ Key capabilities
40
+ - Load/replace the hidden table from a pandas DataFrame (`load_time_steps`).
41
+ - Switch the active time step by `TIME_ID` (`update`).
42
+ - Inspect the current date/time (`display`, `get_date_in_the_past`).
43
+ - Generate a timeline view up to (or strictly before) the current step
44
+ (`generate_timeline`).
45
+ - Prune older steps and renumber so the earliest remaining step becomes 1
46
+ (`prune_time`).
47
+ - Clone from another `TimeManager` (soft link or hard copy) and optionally
48
+ take ownership of the hidden table (`clone_timer`, `take_ownership`).
49
+ - Introspect the active step by parsing the view DDL (`get_current_timeid`,
50
+ `print_view_ddl`).
51
+
52
+ Workflow overview
53
+ 1) Instantiate `TimeManager` with a target `view_name` and `schema_name`.
54
+ 2) Call `load_time_steps(df, time_column)` to (re)create the hidden table and
55
+ point the public view at `TIME_ID = 1`.
56
+ 3) Use `update(time_id)` to switch the active business date.
57
+ 4) (Optional) Create derivative timeline views, prune older steps, or clone.
58
+
59
+ Parameters
60
+ table_name (str): Base public view name to manage (e.g., "MY_VIEW").
61
+ schema_name (str): Teradata schema/database that holds the artifacts.
62
+
63
+ Attributes
64
+ schema_name (str): Target schema for the view and hidden table.
65
+ table_name (str): Hidden table name (`<view_name>_HIDDEN`).
66
+ view_name (str): Public view name (`<view_name>`).
67
+ time_id (str): Name of the step identifier column (default: "time_id").
68
+ nb_time_steps (int | None): Number of steps detected after load/inspection.
69
+ data_type (str | None): SQL data type of `BUSINESS_DATE` (e.g., `DATE`,
70
+ `TIMESTAMP WITH TIME ZONE`), inferred during load/inspection.
71
+
72
+ Notes
73
+ - On initialization, if the hidden table already exists, metadata
74
+ (`data_type`, `nb_time_steps`) is auto-detected.
75
+ - `load_time_steps` will drop and recreate the hidden table to match the
76
+ inferred schema, then rebuild the public view.
77
+ - “Soft” cloning points this manager’s view at the source hidden table;
78
+ “hard” cloning copies the table into this schema and marks it owned.
79
+ - Ownership controls whether `_drop()` is allowed to remove the hidden
80
+ table (use `take_ownership` to promote ownership when appropriate).
42
81
  """
43
82
 
44
83
  def __init__(self, table_name: str, schema_name: str) -> None:
@@ -133,7 +172,8 @@ class TimeManager:
133
172
  logger.debug("Dropped existing table %s.%s (if existed).", self.schema_name, self.table_name)
134
173
  except Exception as e:
135
174
  # Not fatal; the table might not exist. Log at debug when in dev, warning otherwise.
136
- msg = f"Error dropping table {self.schema_name}.{self.table_name}: {e}"
175
+ e_str = str(e).split('\n')[0]
176
+ msg = f"Error dropping table {self.schema_name}.{self.table_name}: {e_str}"
137
177
  if tdfs4ds.DEBUG_MODE:
138
178
  logger.debug(msg)
139
179
  else:
@@ -156,7 +196,7 @@ class TimeManager:
156
196
  schema_name=self.schema_name,
157
197
  if_exists="append"
158
198
  )
159
- logger.info("Inserted %s time steps into %s.%s.", len(df_), self.schema_name, self.table_name)
199
+ logger.info("Inserted %s time steps into %s.%s.", df_.shape[0], self.schema_name, self.table_name)
160
200
 
161
201
  # Step 6: Update view
162
202
  create_view_sql = f"""
@@ -240,12 +280,15 @@ class TimeManager:
240
280
  FROM {self.schema_name}.{self.table_name}
241
281
  WHERE TIME_ID = {time_id}
242
282
  """
243
- if tdfs4ds.DEBUG_MODE:
244
- logger.debug("Executing view update:\n%s", query)
283
+ if getattr(tdfs4ds, "DEBUG_MODE", False):
284
+ logger_safe("debug", "Executing view update:\n%s", query)
285
+
245
286
  tdml.execute_sql(query)
246
- logger.info("Updated view %s.%s to TIME_ID=%s.", self.schema_name, self.view_name, time_id)
287
+ logger_safe("info", "Updated view %s.%s to TIME_ID=%s.", self.schema_name, self.view_name, time_id)
288
+
247
289
  else:
248
- logger.warning(
290
+ logger_safe(
291
+ "warning",
249
292
  "Cannot update view: hidden table %s.%s does not exist.",
250
293
  self.schema_name, self.table_name
251
294
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tdfs4ds
3
- Version: 0.2.4.32
3
+ Version: 0.2.4.34
4
4
  Summary: A python package to simplify the usage of feature store using Teradata Vantage ...
5
5
  Author: Denis Molin
6
6
  Requires-Python: >=3.6
@@ -2,7 +2,7 @@ tdfs/__init__.py,sha256=7AcO7uB1opRCt7t2JOHworKimfAaDeO3boRW7u9Geo8,23
2
2
  tdfs/datasets.py,sha256=-b2MPEKGki2V1M8iUcoDR9uc2krIK7u1CK-EhChvihs,985
3
3
  tdfs/feature_store.py,sha256=Honu7eOAXxP4Ivz0mRlhuNkfTDzgZl5HB1WlQUwzcZ0,31354
4
4
  tdfs/data/curves.csv,sha256=q0Tm-0yu7VMK4lHvHpgi1LMeRq0lO5gJy2Q17brKbEM,112488
5
- tdfs4ds/__init__.py,sha256=cDBV8FsDXDLWddvAHAj6to7GM1MRIvr9IbWTBL16wqA,66290
5
+ tdfs4ds/__init__.py,sha256=Aj0fuEgpIpLho6OOubGRQneD1s7Y8_3isNnZmeB222o,55698
6
6
  tdfs4ds/datasets.py,sha256=LE4Gn0muwdyrIrCrbkE92cnafUML63z1lj5bFIIVzmc,3524
7
7
  tdfs4ds/feature_engineering.py,sha256=oVnZ2V_XNGE12LKC_fNfkrWSQZLgtYRmaf8Dispi6S4,7081
8
8
  tdfs4ds/feature_store.py,sha256=y-oItPZw6nBkBcGAceaATZbkLPTsvpk0OnpzTxYofDs,68576
@@ -17,22 +17,22 @@ tdfs4ds/dataset/dataset.py,sha256=J_fgfsVdR9zSOXrUOqyotqsUD-GlQMGyuld6ueov45w,76
17
17
  tdfs4ds/dataset/dataset_catalog.py,sha256=qxS2thDW2MvsRouSFaX1M0sX2J7IzBAYD8Yf22Tsd5k,16638
18
18
  tdfs4ds/feature_store/__init__.py,sha256=a7NPCkpTx40UR5LRErwnskpABG2Vuib7F5wUjaUGCnI,209
19
19
  tdfs4ds/feature_store/entity_management.py,sha256=9ltytv3yCTG84NZXBpb1Tlkf9pOxvrNb0MVidU4pwvE,10157
20
- tdfs4ds/feature_store/feature_data_processing.py,sha256=rvpnFrV6Tmg8C6xcSQLT_lrFYqZsdSzFXmS-4suK9qg,42847
20
+ tdfs4ds/feature_store/feature_data_processing.py,sha256=7VbrY1FBqK5SbNJYlL3SWKX9t9vwX-yUjkrt9eO9Awc,45017
21
21
  tdfs4ds/feature_store/feature_query_retrieval.py,sha256=51c6ZNlLFiBIxNPinS8ot8bjWEIb1QV2eVg69yzVF80,35381
22
- tdfs4ds/feature_store/feature_store_management.py,sha256=pWM9sjppBgRIg3l1ksoDJsM1fnaZlWtnuE3JuOP_2mY,54736
22
+ tdfs4ds/feature_store/feature_store_management.py,sha256=yXLbINYLA-lzd0t_6TzEe9a8Anlum4x8TRoxZU3FIr8,54276
23
23
  tdfs4ds/process_store/__init__.py,sha256=npHR_xju5ecGmWfYHDyteLwiU3x-cL4HD3sFK_th7xY,229
24
24
  tdfs4ds/process_store/process_followup.py,sha256=PvLcU7meg3ljBlPfuez3qwTVqpHHhVJxYxGqjgiHE8E,7265
25
- tdfs4ds/process_store/process_query_administration.py,sha256=DsIt97cBoJ7NcpQzbQt55eUFNgXGdOMm5Hh2aX5v0PY,7762
26
- tdfs4ds/process_store/process_registration_management.py,sha256=F8VlBoL-de98KnkMvqxnE3WtAYcvWqDtX-KcKiRlhKI,36250
25
+ tdfs4ds/process_store/process_query_administration.py,sha256=AOufkJ6DFUpBiGm-6Q6Dq0Aovw31UGTscZ3Ya0ewS-0,7851
26
+ tdfs4ds/process_store/process_registration_management.py,sha256=2fFjt4Pmh3An1BUFvRX3xABSlQrlWiEiPQStH3A9Xpk,36130
27
27
  tdfs4ds/process_store/process_store_catalog_management.py,sha256=eVUU9uanyXCUkzi2vcHbJPL9qFiXVasnCxPGr-r9EY8,16090
28
28
  tdfs4ds/utils/__init__.py,sha256=-yTMfDLZbQnIRQ64s_bczzT21tDW2A8FZeq9PX5SgFU,168
29
- tdfs4ds/utils/filter_management.py,sha256=wn3glXJTc5Bdb_EZMEiwx06XawgaKiCSIbAXGwG31s0,23226
29
+ tdfs4ds/utils/filter_management.py,sha256=5_8fYYtl8RQgbIi6L_1geNM0wJMm3t1n4QvNA5DnaQg,24760
30
30
  tdfs4ds/utils/info.py,sha256=sShnUxXMlvCtQ6xtShDhqdpTr6sMG0dZQhNBFgUENDY,12058
31
31
  tdfs4ds/utils/lineage.py,sha256=gy5M42qy5fvdWmlohAY3WPYoqAyp5VakeEmeT1YjrJQ,37839
32
32
  tdfs4ds/utils/query_management.py,sha256=nAcE8QY1GWAKgOtb-ubSfDVcnYbU7Ge8CruVRLoPtmY,6356
33
- tdfs4ds/utils/time_management.py,sha256=556DfHpNnCPQzy48p9fBLoupO00ZYZxfG5R_9JCXocU,29094
33
+ tdfs4ds/utils/time_management.py,sha256=asIWvK5K81NNwAGqC-9Tv4Timscxyv0vyuPFs01whu0,31461
34
34
  tdfs4ds/utils/visualization.py,sha256=5S528KoKzzkrAdCxfy7ecyqKvAXBoibNvHwz_u5ISMs,23167
35
- tdfs4ds-0.2.4.32.dist-info/METADATA,sha256=l3dvKqLjImS2cVyZ9jNAXgFkSFHiaDky_KnX3b9Fs_A,14326
36
- tdfs4ds-0.2.4.32.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
37
- tdfs4ds-0.2.4.32.dist-info/top_level.txt,sha256=wMyVkMvnBn8RRt1xBveGQxOpWFijPMPkMiE7G2mi8zo,8
38
- tdfs4ds-0.2.4.32.dist-info/RECORD,,
35
+ tdfs4ds-0.2.4.34.dist-info/METADATA,sha256=7ndN6tocMjqxVwvuu6MaPa_6rU8R2O2wOWXBnpvGO3E,14326
36
+ tdfs4ds-0.2.4.34.dist-info/WHEEL,sha256=R0nc6qTxuoLk7ShA2_Y-UWkN8ZdfDBG2B6Eqpz2WXbs,91
37
+ tdfs4ds-0.2.4.34.dist-info/top_level.txt,sha256=wMyVkMvnBn8RRt1xBveGQxOpWFijPMPkMiE7G2mi8zo,8
38
+ tdfs4ds-0.2.4.34.dist-info/RECORD,,