pointblank 0.13.1__py3-none-any.whl → 0.13.3__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.
pointblank/yaml.py CHANGED
@@ -1,7 +1,8 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from importlib import import_module
3
4
  from pathlib import Path
4
- from typing import Any, Union
5
+ from typing import Any, Iterable, Mapping, Optional, Union
5
6
 
6
7
  import yaml
7
8
  from narwhals.typing import FrameT
@@ -17,7 +18,9 @@ class YAMLValidationError(Exception):
17
18
  pass
18
19
 
19
20
 
20
- def _safe_eval_python_code(code: str) -> Any:
21
+ def _safe_eval_python_code(
22
+ code: str, namespaces: Optional[Union[Iterable[str], Mapping[str, str]]] = None
23
+ ) -> Any:
21
24
  """Safely evaluate Python code with restricted namespace.
22
25
 
23
26
  This function provides a controlled environment for executing Python code embedded in YAML
@@ -68,6 +71,7 @@ def _safe_eval_python_code(code: str) -> Any:
68
71
  "abs": abs,
69
72
  "round": round,
70
73
  "print": print,
74
+ "__import__": __import__,
71
75
  },
72
76
  }
73
77
 
@@ -88,12 +92,25 @@ def _safe_eval_python_code(code: str) -> Any:
88
92
 
89
93
  safe_namespace["pd"] = pd
90
94
 
91
- # Check for dangerous patterns
95
+ if namespaces:
96
+ for alias, module_name in (
97
+ namespaces.items() if isinstance(namespaces, dict) else ((m, m) for m in namespaces)
98
+ ):
99
+ try:
100
+ safe_namespace[alias] = import_module(module_name)
101
+ except ImportError as e:
102
+ raise ImportError(
103
+ f"Could not import requested namespace '{module_name}': {e}"
104
+ ) from e
105
+
106
+ # Check for dangerous patterns and be more specific about __import__ to allow legitimate use
92
107
  dangerous_patterns = [
93
- r"import\s+os",
94
- r"import\s+sys",
95
- r"import\s+subprocess",
96
- r"__import__",
108
+ r"import\s+os\b",
109
+ r"import\s+sys\b",
110
+ r"import\s+subprocess\b",
111
+ r"__import__\s*\(\s*['\"]os['\"]",
112
+ r"__import__\s*\(\s*['\"]sys['\"]",
113
+ r"__import__\s*\(\s*['\"]subprocess['\"]",
97
114
  r"exec\s*\(",
98
115
  r"eval\s*\(",
99
116
  r"open\s*\(",
@@ -142,7 +159,9 @@ def _safe_eval_python_code(code: str) -> Any:
142
159
  raise YAMLValidationError(f"Error executing Python code '{code}': {e}")
143
160
 
144
161
 
145
- def _process_python_expressions(value: Any) -> Any:
162
+ def _process_python_expressions(
163
+ value: Any, namespaces: Optional[Union[Iterable[str], Mapping[str, str]]] = None
164
+ ) -> Any:
146
165
  """Process Python code snippets embedded in YAML values.
147
166
 
148
167
  This function supports the python: block syntax for embedding Python code:
@@ -152,7 +171,7 @@ def _process_python_expressions(value: Any) -> Any:
152
171
  pl.scan_csv("data.csv").head(10)
153
172
 
154
173
  Note: col_vals_expr() also supports a shortcut syntax where the expr parameter
155
- can be written directly without the python: wrapper:
174
+ can be written directly without the python: wrapper: +
156
175
 
157
176
  col_vals_expr:
158
177
  expr: |
@@ -180,14 +199,14 @@ def _process_python_expressions(value: Any) -> Any:
180
199
  # Handle python: block syntax
181
200
  if "python" in value and len(value) == 1:
182
201
  code = value["python"]
183
- return _safe_eval_python_code(code)
202
+ return _safe_eval_python_code(code, namespaces=namespaces)
184
203
 
185
204
  # Recursively process dictionary values
186
- return {k: _process_python_expressions(v) for k, v in value.items()}
205
+ return {k: _process_python_expressions(v, namespaces=namespaces) for k, v in value.items()}
187
206
 
188
207
  elif isinstance(value, list):
189
208
  # Recursively process list items
190
- return [_process_python_expressions(item) for item in value]
209
+ return [_process_python_expressions(item, namespaces=namespaces) for item in value]
191
210
 
192
211
  else:
193
212
  # Return primitive types unchanged
@@ -302,7 +321,7 @@ class YAMLValidator:
302
321
  raise YAMLValidationError("YAML must contain 'steps' field")
303
322
 
304
323
  if not isinstance(config["steps"], list):
305
- raise YAMLValidationError("'steps' must be a list")
324
+ raise YAMLValidationError("'steps' must be a list") # pragma: no cover
306
325
 
307
326
  if len(config["steps"]) == 0:
308
327
  raise YAMLValidationError("'steps' cannot be empty")
@@ -393,9 +412,9 @@ class YAMLValidator:
393
412
  if processed_data is processed_tbl_spec and isinstance(processed_tbl_spec, str):
394
413
  return load_dataset(processed_tbl_spec, tbl_type=df_library)
395
414
  else:
396
- return processed_data
415
+ return processed_data # pragma: no cover
397
416
 
398
- except Exception as e:
417
+ except Exception as e: # pragma: no cover
399
418
  raise YAMLValidationError(f"Failed to load data source '{tbl_spec}': {e}")
400
419
 
401
420
  def _load_csv_file(self, file_path: str, df_library: str) -> Any:
@@ -439,16 +458,16 @@ class YAMLValidator:
439
458
 
440
459
  elif df_library == "duckdb":
441
460
  # For DuckDB, we'll use the existing _process_data since it handles DuckDB
442
- from pointblank.validate import _process_data
461
+ from pointblank.validate import _process_data # pragma: no cover
443
462
 
444
- return _process_data(file_path)
463
+ return _process_data(file_path) # pragma: no cover
445
464
 
446
465
  else:
447
466
  raise YAMLValidationError(
448
467
  f"Unsupported df_library: {df_library}. Use 'polars', 'pandas', or 'duckdb'"
449
468
  )
450
469
 
451
- except Exception as e:
470
+ except Exception as e: # pragma: no cover
452
471
  raise YAMLValidationError(
453
472
  f"Failed to load CSV file '{file_path}' with {df_library}: {e}"
454
473
  )
@@ -547,7 +566,11 @@ class YAMLValidator:
547
566
  f"Schema specification must be a dictionary, got: {type(schema_spec)}"
548
567
  )
549
568
 
550
- def _parse_validation_step(self, step_config: Union[str, dict]) -> tuple[str, dict]:
569
+ def _parse_validation_step(
570
+ self,
571
+ step_config: Union[str, dict],
572
+ namespaces: Optional[Union[Iterable[str], Mapping[str, str]]] = None,
573
+ ) -> tuple[str, dict]:
551
574
  """Parse a single validation step from YAML configuration.
552
575
 
553
576
  Parameters
@@ -598,14 +621,16 @@ class YAMLValidator:
598
621
  # Special case: `col_vals_expr()`'s `expr=` parameter can use shortcut syntax
599
622
  if method_name == "col_vals_expr" and key == "expr" and isinstance(value, str):
600
623
  # Treat string directly as Python code (shortcut syntax)
601
- processed_parameters[key] = _safe_eval_python_code(value)
624
+ processed_parameters[key] = _safe_eval_python_code(value, namespaces=namespaces)
602
625
  # Special case: `pre=` parameter can use shortcut syntax (like `expr=`)
603
626
  elif key == "pre" and isinstance(value, str):
604
627
  # Treat string directly as Python code (shortcut syntax)
605
- processed_parameters[key] = _safe_eval_python_code(value)
628
+ processed_parameters[key] = _safe_eval_python_code(value, namespaces=namespaces)
606
629
  else:
607
630
  # Normal processing (requires python: block syntax)
608
- processed_parameters[key] = _process_python_expressions(value)
631
+ processed_parameters[key] = _process_python_expressions(
632
+ value, namespaces=namespaces
633
+ )
609
634
  parameters = processed_parameters
610
635
 
611
636
  # Convert `columns=` specification
@@ -634,7 +659,7 @@ class YAMLValidator:
634
659
  if isinstance(expr, str):
635
660
  lambda_expressions.append(_safe_eval_python_code(expr))
636
661
  else:
637
- lambda_expressions.append(expr)
662
+ lambda_expressions.append(expr) # pragma: no cover
638
663
  # Pass expressions as positional arguments (stored as special key)
639
664
  parameters["_conjointly_expressions"] = lambda_expressions
640
665
  else:
@@ -658,7 +683,9 @@ class YAMLValidator:
658
683
 
659
684
  return self.validation_method_map[method_name], parameters
660
685
 
661
- def build_validation(self, config: dict) -> Validate:
686
+ def build_validation(
687
+ self, config: dict, namespaces: Optional[Union[Iterable[str], Mapping[str, str]]] = None
688
+ ) -> Validate:
662
689
  """Convert YAML config to Validate object.
663
690
 
664
691
  Parameters
@@ -693,7 +720,9 @@ class YAMLValidator:
693
720
  # Set actions if provided
694
721
  if "actions" in config:
695
722
  # Process actions: handle `python:` block syntax for callables
696
- processed_actions = _process_python_expressions(config["actions"])
723
+ processed_actions = _process_python_expressions(
724
+ config["actions"], namespaces=namespaces
725
+ )
697
726
  # Convert to Actions object
698
727
  validate_kwargs["actions"] = Actions(**processed_actions)
699
728
 
@@ -713,7 +742,9 @@ class YAMLValidator:
713
742
 
714
743
  # Add validation steps
715
744
  for step_config in config["steps"]:
716
- method_name, parameters = self._parse_validation_step(step_config)
745
+ method_name, parameters = self._parse_validation_step(
746
+ step_config, namespaces=namespaces
747
+ )
717
748
 
718
749
  # Get the method from the validation object
719
750
  method = getattr(validation, method_name)
@@ -728,7 +759,9 @@ class YAMLValidator:
728
759
 
729
760
  return validation
730
761
 
731
- def execute_workflow(self, config: dict) -> Validate:
762
+ def execute_workflow(
763
+ self, config: dict, namespaces: Optional[Union[Iterable[str], Mapping[str, str]]] = None
764
+ ) -> Validate:
732
765
  """Execute a complete YAML validation workflow.
733
766
 
734
767
  Parameters
@@ -742,7 +775,7 @@ class YAMLValidator:
742
775
  Interrogated Validate object with results.
743
776
  """
744
777
  # Build the validation plan
745
- validation = self.build_validation(config)
778
+ validation = self.build_validation(config, namespaces=namespaces)
746
779
 
747
780
  # Execute interrogation to get results
748
781
  validation = validation.interrogate()
@@ -750,7 +783,11 @@ class YAMLValidator:
750
783
  return validation
751
784
 
752
785
 
753
- def yaml_interrogate(yaml: Union[str, Path], set_tbl: Union[FrameT, Any, None] = None) -> Validate:
786
+ def yaml_interrogate(
787
+ yaml: Union[str, Path],
788
+ set_tbl: Union[FrameT, Any, None] = None,
789
+ namespaces: Optional[Union[Iterable[str], Mapping[str, str]]] = None,
790
+ ) -> Validate:
754
791
  """Execute a YAML-based validation workflow.
755
792
 
756
793
  This is the main entry point for YAML-based validation workflows. It takes YAML configuration
@@ -772,6 +809,10 @@ def yaml_interrogate(yaml: Union[str, Path], set_tbl: Union[FrameT, Any, None] =
772
809
  `tbl` field before executing the validation workflow. This can be any supported table type
773
810
  including DataFrame objects, Ibis table objects, CSV file paths, Parquet file paths, GitHub
774
811
  URLs, or database connection strings.
812
+ namespaces
813
+ Optional module namespaces to make available for Python code execution in YAML
814
+ configurations. Can be a dictionary mapping aliases to module names or a list of module
815
+ names. See the "Using Namespaces" section below for detailed examples.
775
816
 
776
817
  Returns
777
818
  -------
@@ -786,6 +827,71 @@ def yaml_interrogate(yaml: Union[str, Path], set_tbl: Union[FrameT, Any, None] =
786
827
  If the YAML is invalid, malformed, or execution fails. This includes syntax errors, missing
787
828
  required fields, unknown validation methods, or data loading failures.
788
829
 
830
+ Using Namespaces
831
+ ----------------
832
+ The `namespaces=` parameter enables custom Python modules and functions in YAML configurations.
833
+ This is particularly useful for custom action functions and advanced Python expressions.
834
+
835
+ **Namespace formats:**
836
+
837
+ - Dictionary format: `{"alias": "module.name"}` maps aliases to module names
838
+ - List format: `["module.name", "another.module"]` imports modules directly
839
+
840
+ **Option 1: Inline expressions (no namespaces needed)**
841
+
842
+ ```{python}
843
+ import pointblank as pb
844
+
845
+ # Simple inline custom action
846
+ yaml_config = '''
847
+ tbl: small_table
848
+ thresholds:
849
+ warning: 0.01
850
+ actions:
851
+ warning:
852
+ python: "lambda: print('Custom warning triggered')"
853
+ steps:
854
+ - col_vals_gt:
855
+ columns: [a]
856
+ value: 1000
857
+ '''
858
+
859
+ result = pb.yaml_interrogate(yaml_config)
860
+ result
861
+ ```
862
+
863
+ **Option 2: External functions with namespaces**
864
+
865
+ ```{python}
866
+ # Define a custom action function
867
+ def my_custom_action():
868
+ print("Data validation failed: please check your data.")
869
+
870
+ # Add to current module for demo
871
+ import sys
872
+ sys.modules[__name__].my_custom_action = my_custom_action
873
+
874
+ # YAML that references the external function
875
+ yaml_config = '''
876
+ tbl: small_table
877
+ thresholds:
878
+ warning: 0.01
879
+ actions:
880
+ warning:
881
+ python: actions.my_custom_action
882
+ steps:
883
+ - col_vals_gt:
884
+ columns: [a]
885
+ value: 1000 # This will fail
886
+ '''
887
+
888
+ # Use namespaces to make the function available
889
+ result = pb.yaml_interrogate(yaml_config, namespaces={'actions': '__main__'})
890
+ result
891
+ ```
892
+
893
+ This approach enables modular, reusable validation workflows with custom business logic.
894
+
789
895
  Examples
790
896
  --------
791
897
  ```{python}
@@ -928,14 +1034,14 @@ def yaml_interrogate(yaml: Union[str, Path], set_tbl: Union[FrameT, Any, None] =
928
1034
  # If `set_tbl=` is provided, we need to build the validation workflow and then use `set_tbl()`
929
1035
  if set_tbl is not None:
930
1036
  # First build the validation object without interrogation
931
- validation = validator.build_validation(config)
1037
+ validation = validator.build_validation(config, namespaces=namespaces)
932
1038
  # Then replace the table using set_tbl method
933
1039
  validation = validation.set_tbl(tbl=set_tbl)
934
1040
  # Finally interrogate with the new table
935
1041
  return validation.interrogate()
936
1042
  else:
937
1043
  # Standard execution without table override (includes interrogation)
938
- return validator.execute_workflow(config)
1044
+ return validator.execute_workflow(config, namespaces=namespaces)
939
1045
 
940
1046
 
941
1047
  def load_yaml_config(file_path: Union[str, Path]) -> dict:
@@ -1223,7 +1329,7 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1223
1329
  """
1224
1330
  # First, parse the raw YAML to detect Polars/Pandas expressions in the source code
1225
1331
  if isinstance(yaml, Path):
1226
- yaml_content = yaml.read_text()
1332
+ yaml_content = yaml.read_text() # pragma: no cover
1227
1333
  elif isinstance(yaml, str):
1228
1334
  # Check if it's a file path (single line, reasonable length, no newlines)
1229
1335
  if len(yaml) < 260 and "\n" not in yaml and Path(yaml).exists():
@@ -1231,7 +1337,7 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1231
1337
  else:
1232
1338
  yaml_content = yaml
1233
1339
  else:
1234
- yaml_content = str(yaml)
1340
+ yaml_content = str(yaml) # pragma: no cover
1235
1341
 
1236
1342
  # Track whether we need to import Polars and Pandas by analyzing the raw YAML content
1237
1343
  needs_polars_import = False
@@ -1326,7 +1432,7 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1326
1432
  validate_args.append(f'data=pb.load_dataset("{tbl_spec}", tbl_type="{df_library}")')
1327
1433
  else:
1328
1434
  # Fallback to placeholder if we couldn't extract the original expression
1329
- validate_args.append("data=<python_expression_result>")
1435
+ validate_args.append("data=<python_expression_result>") # pragma: no cover
1330
1436
 
1331
1437
  # Add table name if present
1332
1438
  if "tbl_name" in config:
@@ -1359,7 +1465,7 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1359
1465
  action_params.append(f'{key}="{value}"')
1360
1466
  else:
1361
1467
  # For callables or complex expressions, use placeholder
1362
- action_params.append(f"{key}={value}")
1468
+ action_params.append(f"{key}={value}") # pragma: no cover
1363
1469
  actions_str = "pb.Actions(" + ", ".join(action_params) + ")"
1364
1470
  validate_args.append(f"actions={actions_str}")
1365
1471
 
@@ -1414,7 +1520,7 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1414
1520
  elif isinstance(step_params["expr"], str):
1415
1521
  original_expressions["expr"] = step_params["expr"]
1416
1522
 
1417
- method_name, parameters = validator._parse_validation_step(step_config)
1523
+ method_name, parameters = validator._parse_validation_step(step_config, namespaces=None)
1418
1524
 
1419
1525
  # Apply the original expressions to override the converted lambda functions
1420
1526
  if method_name == "conjointly" and "expressions" in original_expressions:
@@ -1446,13 +1552,13 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1446
1552
  expressions_str = "[" + ", ".join([f'"{expr}"' for expr in value]) + "]"
1447
1553
  param_parts.append(f"expressions={expressions_str}")
1448
1554
  else:
1449
- param_parts.append(f"expressions={value}")
1555
+ param_parts.append(f"expressions={value}") # pragma: no cover
1450
1556
  elif key == "expr" and method_name == "specially":
1451
1557
  # Handle specially expr parameter: should be unquoted lambda expression
1452
1558
  if isinstance(value, str):
1453
1559
  param_parts.append(f"expr={value}")
1454
1560
  else:
1455
- param_parts.append(f"expr={value}")
1561
+ param_parts.append(f"expr={value}") # pragma: no cover
1456
1562
  elif key in ["columns", "columns_subset"]:
1457
1563
  if isinstance(value, list):
1458
1564
  if len(value) == 1:
@@ -1463,7 +1569,7 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1463
1569
  columns_str = "[" + ", ".join([f'"{col}"' for col in value]) + "]"
1464
1570
  param_parts.append(f"{key}={columns_str}")
1465
1571
  else:
1466
- param_parts.append(f'{key}="{value}"')
1572
+ param_parts.append(f'{key}="{value}"') # pragma: no cover
1467
1573
  elif key == "brief":
1468
1574
  # Handle `brief=` parameter: can be a boolean or a string
1469
1575
  if isinstance(value, bool):
@@ -1486,25 +1592,29 @@ def yaml_to_python(yaml: Union[str, Path]) -> str:
1486
1592
  elif isinstance(value.warning, list) and len(value.warning) == 1:
1487
1593
  action_params.append(f'warning="{value.warning[0]}"')
1488
1594
  else:
1489
- action_params.append(f"warning={value.warning}")
1595
+ action_params.append(f"warning={value.warning}") # pragma: no cover
1490
1596
 
1491
1597
  if value.error is not None:
1492
1598
  error_expr_path = f"{step_action_base}.error"
1493
1599
  if error_expr_path in step_expressions:
1494
- action_params.append(f"error={step_expressions[error_expr_path]}")
1600
+ action_params.append(
1601
+ f"error={step_expressions[error_expr_path]}"
1602
+ ) # pragma: no cover
1495
1603
  elif isinstance(value.error, list) and len(value.error) == 1:
1496
1604
  action_params.append(f'error="{value.error[0]}"')
1497
1605
  else:
1498
- action_params.append(f"error={value.error}")
1606
+ action_params.append(f"error={value.error}") # pragma: no cover
1499
1607
 
1500
1608
  if value.critical is not None:
1501
1609
  critical_expr_path = f"{step_action_base}.critical"
1502
1610
  if critical_expr_path in step_expressions:
1503
- action_params.append(f"critical={step_expressions[critical_expr_path]}")
1611
+ action_params.append(
1612
+ f"critical={step_expressions[critical_expr_path]}"
1613
+ ) # pragma: no cover
1504
1614
  elif isinstance(value.critical, list) and len(value.critical) == 1:
1505
1615
  action_params.append(f'critical="{value.critical[0]}"')
1506
1616
  else:
1507
- action_params.append(f"critical={value.critical}")
1617
+ action_params.append(f"critical={value.critical}") # pragma: no cover
1508
1618
 
1509
1619
  if hasattr(value, "highest_only") and value.highest_only is not True:
1510
1620
  action_params.append(f"highest_only={value.highest_only}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pointblank
3
- Version: 0.13.1
3
+ Version: 0.13.3
4
4
  Summary: Find out if your data is what you think it is.
5
5
  Author-email: Richard Iannone <riannone@me.com>
6
6
  License: MIT License
@@ -52,7 +52,7 @@ Requires-Dist: pyyaml>=6.0.0
52
52
  Provides-Extra: pd
53
53
  Requires-Dist: pandas>=2.2.3; extra == "pd"
54
54
  Provides-Extra: pl
55
- Requires-Dist: polars>=1.24.0; extra == "pl"
55
+ Requires-Dist: polars>=1.33.0; extra == "pl"
56
56
  Provides-Extra: pyspark
57
57
  Requires-Dist: pyspark==3.5.6; extra == "pyspark"
58
58
  Provides-Extra: generate
@@ -91,6 +91,7 @@ Requires-Dist: pandas>=2.2.3; extra == "docs"
91
91
  Requires-Dist: polars>=1.17.1; extra == "docs"
92
92
  Requires-Dist: pyspark==3.5.6; extra == "docs"
93
93
  Requires-Dist: openpyxl>=3.0.0; extra == "docs"
94
+ Requires-Dist: duckdb<1.3.3,>=1.2.0; extra == "docs"
94
95
  Dynamic: license-file
95
96
 
96
97
  <div align="center">
@@ -1,9 +1,9 @@
1
- pointblank/__init__.py,sha256=Qt6HEcSoduSkphatxf2Bqy4KNP-wXssRv9zlEslhTJk,1915
2
- pointblank/_constants.py,sha256=rB8qTnhabwmSQURevHqokC1pp5lfaWMCzhmbMZ0CP8A,81512
1
+ pointblank/__init__.py,sha256=IZ-JwGWr2DOFBfq4XvE0SzceWMkFewtBN10V4F9pIGg,1876
2
+ pointblank/_constants.py,sha256=HHiGuso_To-PsKQLxVDYa_Iczy92iPoIYurTy6BAtL4,80778
3
3
  pointblank/_constants_docs.py,sha256=JBmtt16zTYQ-zaM4ElLExtKs-dKlnN553Ys2ML1Y1C8,2099
4
- pointblank/_constants_translations.py,sha256=HXcCYmKoMjoaFv-Ym4UWv3AsIVXik2zDyAy7xvTvv0Y,186710
4
+ pointblank/_constants_translations.py,sha256=NBVGX9veULXMwEHwSrlzpCpM9hcFeyS1xBmyBt_8_mU,190053
5
5
  pointblank/_datascan_utils.py,sha256=EMfeabXm_ZsCUKPROB7rFhyOpjtRs8jcnZ_9nBtMyws,1750
6
- pointblank/_interrogation.py,sha256=p3qPTgcsYiDEyV9d5pWLzAqz9rU9-IsfmSFV4sWRBNI,76932
6
+ pointblank/_interrogation.py,sha256=FbzFa_QJIYQjH0l4D6XaMW73r71gwMrfA8lXiF4r2a4,76540
7
7
  pointblank/_typing.py,sha256=aItbCbzhbzqjK3lCbL27ltRyXoAH1c3-U6xQdRzg-lU,1594
8
8
  pointblank/_utils.py,sha256=ikgkFomoAEOxaiItHZUo3NTHu0MJHWfKAF_fnX9rRnA,30685
9
9
  pointblank/_utils_check_args.py,sha256=rFEc1nbCN8ftsQQWVjCNWmQ2QmUDxkfgmoJclrZeTLs,5489
@@ -11,18 +11,17 @@ pointblank/_utils_html.py,sha256=uJWvS9JwQVEZgwsGmScA_u_EBRND75rzUvnJPalbRVs,373
11
11
  pointblank/actions.py,sha256=D6o9B2_ES9PNQg9HZwREacrrt-3A5bhdrBkL1UXz__s,18281
12
12
  pointblank/assistant.py,sha256=vcdFgXmdVSiylQgTW3e62c13X3vAicBPjyG8OTZXmGM,15902
13
13
  pointblank/cli.py,sha256=p2cO-NKInQ41hqRMy6TNmxOj48pR5vq8s7JTLXfcP4M,228188
14
- pointblank/column.py,sha256=_FJjpjv760D1p6YGgqbwmKYktouG7AJ2A9uIMYQBTYA,76560
14
+ pointblank/column.py,sha256=w41CBg3lt3ZzKS_j-SYdkRmnOWcP5xXa0rRjsPVRE_M,76781
15
15
  pointblank/compare.py,sha256=kFd18CehHz7g-2MF1kSmJSdOoAP80q_9PaF6QzHC1ds,866
16
- pointblank/datascan.py,sha256=nmTcRLW8nAZfvRS_Nf00Wgx4oUX-o6WFOZqLDbedbu8,24563
16
+ pointblank/datascan.py,sha256=tQiwe9x0jAbK9OJLQe53R_qRl2uBKireaWsMlM8K1sw,24630
17
17
  pointblank/draft.py,sha256=cusr4fBiNncCKIOU8UwvJcvkBeBuUnqH_UfYp9dtNss,15777
18
18
  pointblank/scan_profile.py,sha256=lZU5hlnzznDATNn9W3gNdyuFm05WDP8y1RjDJEcE5zg,10426
19
19
  pointblank/scan_profile_stats.py,sha256=qdzoGXB-zi2hmpA4mTz6LLTqMnb-NRG9ndxU9cxS72w,4461
20
- pointblank/schema.py,sha256=vwGF8UKy2riRSQzcwatcI6L0t_6ccdbOayrKonvyodE,45777
20
+ pointblank/schema.py,sha256=hjALMuYppNfELC_nAqfM9fLjPdN1w2M3rDMusrPqFYA,50757
21
21
  pointblank/segments.py,sha256=RXp3lPr3FboVseadNqLgIeoMBh_mykrQSFp1WtV41Yg,5570
22
- pointblank/tf.py,sha256=8o_8m4i01teulEe3-YYMotSNf3tImjBMInsvdjSAO5Q,8844
23
22
  pointblank/thresholds.py,sha256=mybeLzTVdmN04NLKoV-jiSBXsWknwHO0Gox0ttVN_MU,25766
24
- pointblank/validate.py,sha256=py6w239Mh7tbAfXJkanDLARCkWE5EFhTlfvS0KOjnWA,697215
25
- pointblank/yaml.py,sha256=Sy802CZBOgEZGwbIes8wcXPPt2a5rXO0b3lh9tsLS8w,58966
23
+ pointblank/validate.py,sha256=v4jzFOYufrck_3CPIz4Jo53Y_5VYYTTFcqMq6B4LttY,713196
24
+ pointblank/yaml.py,sha256=cHwDvybhp_oLOGR1rA83trEDQWYuRGhT4iEa6FMXi6w,63074
26
25
  pointblank/data/api-docs.txt,sha256=w2nIkIL_fJpXlPR9clogqcgdiv-uHvdSDI8gjkP_mCQ,531711
27
26
  pointblank/data/game_revenue-duckdb.zip,sha256=tKIVx48OGLYGsQPS3h5AjA2Nyq_rfEpLCjBiFUWhagU,35880
28
27
  pointblank/data/game_revenue.zip,sha256=7c9EvHLyi93CHUd4p3dM4CZ-GucFCtXKSPxgLojL32U,33749
@@ -33,9 +32,9 @@ pointblank/data/nycflights.zip,sha256=yVjbUaKUz2LydSdF9cABuir0VReHBBgV7shiNWSd0m
33
32
  pointblank/data/polars-api-docs.txt,sha256=KGcS-BOtUs9zgpkWfXD-GFdFh4O_zjdkpX7msHjztLg,198045
34
33
  pointblank/data/small_table-duckdb.zip,sha256=BhTaZ2CRS4-9Z1uVhOU6HggvW3XCar7etMznfENIcOc,2028
35
34
  pointblank/data/small_table.zip,sha256=lmFb90Nb-v5X559Ikjg31YLAXuRyMkD9yLRElkXPMzQ,472
36
- pointblank-0.13.1.dist-info/licenses/LICENSE,sha256=apLF-HWPNU7pT5bmf5KmZpD5Cklpy2u-BN_0xBoRMLY,1081
37
- pointblank-0.13.1.dist-info/METADATA,sha256=nuywCyQQooecMF-WiWVnM-AHN2kCL0roQgWi5s2hSwU,19529
38
- pointblank-0.13.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
39
- pointblank-0.13.1.dist-info/entry_points.txt,sha256=GqqqOTOH8uZe22wLcvYjzpizqk_j4MNcUo2YM14ryCw,42
40
- pointblank-0.13.1.dist-info/top_level.txt,sha256=-wHrS1SvV8-nhvc3w-PPYs1C1WtEc1pK-eGjubbCCKc,11
41
- pointblank-0.13.1.dist-info/RECORD,,
35
+ pointblank-0.13.3.dist-info/licenses/LICENSE,sha256=apLF-HWPNU7pT5bmf5KmZpD5Cklpy2u-BN_0xBoRMLY,1081
36
+ pointblank-0.13.3.dist-info/METADATA,sha256=jXGDWi-DW5kAdRyUTjgVfRTB-6tMgyYd-uqeeyCvvKk,19582
37
+ pointblank-0.13.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
38
+ pointblank-0.13.3.dist-info/entry_points.txt,sha256=GqqqOTOH8uZe22wLcvYjzpizqk_j4MNcUo2YM14ryCw,42
39
+ pointblank-0.13.3.dist-info/top_level.txt,sha256=-wHrS1SvV8-nhvc3w-PPYs1C1WtEc1pK-eGjubbCCKc,11
40
+ pointblank-0.13.3.dist-info/RECORD,,