openlinktoken-pyspark 2.0.0__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.
@@ -0,0 +1,28 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """
3
+ Open Link Token PySpark Bridge - Distributed token generation for PySpark DataFrames.
4
+ """
5
+
6
+ __version__ = "2.0.0"
7
+
8
+ from openlinktoken_pyspark.notebook_helpers import (
9
+ CustomTokenDefinition,
10
+ TokenBuilder,
11
+ create_token_generator,
12
+ create_token_generator_from_exchange_config,
13
+ quick_token,
14
+ quick_token_from_exchange_config,
15
+ )
16
+ from openlinktoken_pyspark.overlap_analyzer import OpenLinkTokenOverlapAnalyzer
17
+ from openlinktoken_pyspark.token_processor import OpenLinkTokenProcessor
18
+
19
+ __all__ = [
20
+ "CustomTokenDefinition",
21
+ "TokenBuilder",
22
+ "OpenLinkTokenProcessor",
23
+ "OpenLinkTokenOverlapAnalyzer",
24
+ "create_token_generator",
25
+ "create_token_generator_from_exchange_config",
26
+ "quick_token",
27
+ "quick_token_from_exchange_config",
28
+ ]
@@ -0,0 +1,356 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """
3
+ Notebook helpers for convenient token definition and experimentation.
4
+ This module provides a simplified API for creating custom tokens in Jupyter notebooks.
5
+ """
6
+
7
+ from pathlib import Path
8
+ from typing import Any, Dict, List, Mapping, Optional, Type, Union
9
+
10
+ from openlinktoken.attributes.attribute import Attribute
11
+ from openlinktoken.attributes.attribute_expression import AttributeExpression
12
+ from openlinktoken.attributes.general.record_id_attribute import RecordIdAttribute
13
+ from openlinktoken.attributes.person.birth_date_attribute import BirthDateAttribute
14
+ from openlinktoken.attributes.person.first_name_attribute import FirstNameAttribute
15
+ from openlinktoken.attributes.person.last_name_attribute import LastNameAttribute
16
+ from openlinktoken.attributes.person.postal_code_attribute import PostalCodeAttribute
17
+ from openlinktoken.attributes.person.sex_attribute import SexAttribute
18
+ from openlinktoken.attributes.person.social_security_number_attribute import SocialSecurityNumberAttribute
19
+ from openlinktoken.exchange_config import derive_transport_encryption_key, resolve_exchange_config_inputs
20
+ from openlinktoken.tokens.base_token_definition import BaseTokenDefinition
21
+ from openlinktoken.tokens.token import Token
22
+ from openlinktoken.tokens.token_generator import TokenGenerator
23
+ from openlinktoken.tokentransformer.encrypt_token_transformer import EncryptTokenTransformer
24
+ from openlinktoken.tokentransformer.hash_token_transformer import HashTokenTransformer
25
+
26
+ # Convenient attribute name mappings
27
+ ATTRIBUTE_MAP = {
28
+ "first_name": FirstNameAttribute,
29
+ "last_name": LastNameAttribute,
30
+ "birth_date": BirthDateAttribute,
31
+ "sex": SexAttribute,
32
+ "postal_code": PostalCodeAttribute,
33
+ "ssn": SocialSecurityNumberAttribute,
34
+ "record_id": RecordIdAttribute,
35
+ }
36
+
37
+
38
+ class TokenBuilder:
39
+ """
40
+ A fluent builder for creating custom tokens with minimal code.
41
+
42
+ Example:
43
+ >>> token = TokenBuilder("T6") \\
44
+ ... .add("last_name", "T|U") \\
45
+ ... .add("first_name", "T|U") \\
46
+ ... .add("birth_date", "T|D") \\
47
+ ... .add("postal_code", "T|S(0,3)") \\
48
+ ... .add("sex", "T|U") \\
49
+ ... .build()
50
+ """
51
+
52
+ def __init__(self, token_id: str):
53
+ """
54
+ Initialize the token builder.
55
+
56
+ Args:
57
+ token_id: The identifier for the token (e.g., "T6", "T7").
58
+ """
59
+ self.token_id = token_id
60
+ self.expressions: List[AttributeExpression] = []
61
+
62
+ def add(self, attribute: Union[str, Type[Attribute]], expression: str = "T") -> "TokenBuilder":
63
+ """
64
+ Add an attribute expression to the token definition.
65
+
66
+ Args:
67
+ attribute: Either an attribute class or a string name (e.g., "first_name", "last_name").
68
+ expression: The transformation expression (e.g., "T|U", "T|S(0,3)|U").
69
+
70
+ Returns:
71
+ Self for method chaining.
72
+
73
+ Example:
74
+ >>> builder.add("last_name", "T|U").add("first_name", "T|S(0,3)|U")
75
+ """
76
+ if isinstance(attribute, str):
77
+ if attribute not in ATTRIBUTE_MAP:
78
+ raise ValueError(f"Unknown attribute: {attribute}. Available: {', '.join(ATTRIBUTE_MAP.keys())}")
79
+ attribute_class = ATTRIBUTE_MAP[attribute]
80
+ else:
81
+ attribute_class = attribute
82
+
83
+ self.expressions.append(AttributeExpression(attribute_class, expression))
84
+ return self
85
+
86
+ def build(self) -> Token:
87
+ """
88
+ Build the custom token.
89
+
90
+ Returns:
91
+ A Token instance with the configured expressions.
92
+ """
93
+ token_id = self.token_id
94
+ expressions = self.expressions
95
+
96
+ class CustomToken(Token):
97
+ """Dynamically created custom token."""
98
+
99
+ ID = token_id
100
+
101
+ def __init__(self):
102
+ self._definition = expressions
103
+
104
+ def get_identifier(self):
105
+ return self.ID
106
+
107
+ def get_definition(self):
108
+ return self._definition
109
+
110
+ return CustomToken()
111
+
112
+
113
+ class CustomTokenDefinition(BaseTokenDefinition):
114
+ """
115
+ A custom token definition that can include multiple custom tokens.
116
+
117
+ Example:
118
+ >>> definition = CustomTokenDefinition()
119
+ >>> definition.add_token(t6_token)
120
+ >>> definition.add_token(t7_token)
121
+ """
122
+
123
+ def __init__(self):
124
+ """Initialize an empty custom token definition."""
125
+ self.tokens: Dict[str, Token] = {}
126
+
127
+ def add_token(self, token: Token) -> "CustomTokenDefinition":
128
+ """
129
+ Add a custom token to the definition.
130
+
131
+ Args:
132
+ token: The token to add.
133
+
134
+ Returns:
135
+ Self for method chaining.
136
+ """
137
+ token_id = token.get_identifier()
138
+ self.tokens[token_id] = token
139
+ return self
140
+
141
+ def get_version(self) -> str:
142
+ """Get the version of the token definition."""
143
+ return "2.0-custom"
144
+
145
+ def get_token_identifiers(self) -> set:
146
+ """Get all token identifiers."""
147
+ return set(self.tokens.keys())
148
+
149
+ def get_token_definition(self, token_id: str) -> List[AttributeExpression]:
150
+ """Get the token definition for a given token identifier."""
151
+ token = self.tokens.get(token_id)
152
+ if token:
153
+ return token.get_definition()
154
+ return None
155
+
156
+
157
+ def create_token_generator(
158
+ hashing_secret: Union[str, bytes, None],
159
+ encryption_key: Union[str, bytes],
160
+ token_definition: Optional[BaseTokenDefinition] = None,
161
+ ) -> TokenGenerator:
162
+ """
163
+ Create a token generator with the specified secrets and token definition.
164
+
165
+ Args:
166
+ hashing_secret: The secret used for HMAC-SHA256 hashing.
167
+ encryption_key: The 32-character key used for AES-256 encryption.
168
+ token_definition: Optional custom token definition. If None, uses default tokens.
169
+
170
+ Returns:
171
+ A configured TokenGenerator instance.
172
+
173
+ Example:
174
+ >>> # Use default tokens (T1-T5)
175
+ >>> generator = create_token_generator("my-hash-secret", "my-32-character-encryption-key!")
176
+ >>>
177
+ >>> # Use custom tokens
178
+ >>> custom_def = CustomTokenDefinition().add_token(t6_token)
179
+ >>> generator = create_token_generator("secret", "key123...", custom_def)
180
+ """
181
+ if token_definition is None:
182
+ from openlinktoken.tokens.token_definition import TokenDefinition
183
+
184
+ token_definition = TokenDefinition()
185
+
186
+ token_transformers = [HashTokenTransformer(hashing_secret), EncryptTokenTransformer(encryption_key)]
187
+
188
+ return TokenGenerator.from_transformers(token_definition, token_transformers)
189
+
190
+
191
+ def create_token_generator_from_exchange_config(
192
+ exchange_config_path: Union[str, Path, None] = None,
193
+ exchange_config_value: Union[str, bytes, Mapping[str, Any], None] = None,
194
+ private_key_path: Union[str, Path, None] = None,
195
+ private_key_env: Optional[str] = None,
196
+ private_key_value: Union[str, bytes, None] = None,
197
+ token_definition: Optional[BaseTokenDefinition] = None,
198
+ ) -> TokenGenerator:
199
+ """
200
+ Create a token generator from an exchange config and private-key inputs.
201
+
202
+ Args:
203
+ exchange_config_path: Optional exchange-config path. Uses the default path when omitted.
204
+ exchange_config_value: Optional in-memory exchange-config JSON or decoded mapping.
205
+ private_key_path: Optional private-key PEM path.
206
+ private_key_env: Optional environment-variable name containing private-key PEM text.
207
+ private_key_value: Optional in-memory private-key PEM text or bytes.
208
+ token_definition: Optional custom token definition. If None, uses default tokens.
209
+
210
+ Returns:
211
+ A configured TokenGenerator instance using the exchange hashing secret and transport key.
212
+ """
213
+ exchange = resolve_exchange_config_inputs(
214
+ exchange_config_path=exchange_config_path,
215
+ exchange_config_value=exchange_config_value,
216
+ private_key_path=private_key_path,
217
+ private_key_env=private_key_env,
218
+ private_key_value=private_key_value,
219
+ )
220
+ return create_token_generator(
221
+ hashing_secret=exchange.hashing_secret,
222
+ encryption_key=derive_transport_encryption_key(exchange),
223
+ token_definition=token_definition,
224
+ )
225
+
226
+
227
+ def quick_token(
228
+ token_id: str,
229
+ attributes: List[tuple],
230
+ hashing_secret: Union[str, bytes, None],
231
+ encryption_key: Union[str, bytes],
232
+ ) -> TokenGenerator:
233
+ """
234
+ Create a custom token and generator in one quick call.
235
+
236
+ Args:
237
+ token_id: The identifier for the new token (e.g., "T6").
238
+ attributes: List of (attribute_name, expression) tuples.
239
+ hashing_secret: The secret used for HMAC-SHA256 hashing.
240
+ encryption_key: The 32-character key used for AES-256 encryption.
241
+
242
+ Returns:
243
+ A TokenGenerator configured with the custom token.
244
+
245
+ Example:
246
+ >>> generator = quick_token(
247
+ ... "T6",
248
+ ... [
249
+ ... ("last_name", "T|U"),
250
+ ... ("first_name", "T|U"),
251
+ ... ("birth_date", "T|D"),
252
+ ... ("postal_code", "T|S(0,3)"),
253
+ ... ("sex", "T|U")
254
+ ... ],
255
+ ... "my-hashing-secret",
256
+ ... "my-32-character-encryption-key!"
257
+ ... )
258
+ """
259
+ builder = TokenBuilder(token_id)
260
+ for attr_name, expr in attributes:
261
+ builder.add(attr_name, expr)
262
+
263
+ token = builder.build()
264
+ definition = CustomTokenDefinition().add_token(token)
265
+
266
+ return create_token_generator(hashing_secret, encryption_key, definition)
267
+
268
+
269
+ def quick_token_from_exchange_config(
270
+ token_id: str,
271
+ attributes: List[tuple],
272
+ exchange_config_path: Union[str, Path, None] = None,
273
+ exchange_config_value: Union[str, bytes, Mapping[str, Any], None] = None,
274
+ private_key_path: Union[str, Path, None] = None,
275
+ private_key_env: Optional[str] = None,
276
+ private_key_value: Union[str, bytes, None] = None,
277
+ ) -> TokenGenerator:
278
+ """
279
+ Create a custom token generator from exchange-config inputs in one quick call.
280
+
281
+ Args:
282
+ token_id: The identifier for the new token (e.g., "T6").
283
+ attributes: List of (attribute_name, expression) tuples.
284
+ exchange_config_path: Optional exchange-config path. Uses the default path when omitted.
285
+ exchange_config_value: Optional in-memory exchange-config JSON or decoded mapping.
286
+ private_key_path: Optional private-key PEM path.
287
+ private_key_env: Optional environment-variable name containing private-key PEM text.
288
+ private_key_value: Optional in-memory private-key PEM text or bytes.
289
+
290
+ Returns:
291
+ A TokenGenerator configured with the custom token and exchange-derived secrets.
292
+ """
293
+ builder = TokenBuilder(token_id)
294
+ for attr_name, expr in attributes:
295
+ builder.add(attr_name, expr)
296
+
297
+ token = builder.build()
298
+ definition = CustomTokenDefinition().add_token(token)
299
+
300
+ return create_token_generator_from_exchange_config(
301
+ exchange_config_path=exchange_config_path,
302
+ exchange_config_value=exchange_config_value,
303
+ private_key_path=private_key_path,
304
+ private_key_env=private_key_env,
305
+ private_key_value=private_key_value,
306
+ token_definition=definition,
307
+ )
308
+
309
+
310
+ # Convenience function to list available attributes
311
+ def list_attributes() -> Dict[str, Type[Attribute]]:
312
+ """
313
+ Get a dictionary of available attribute names and their classes.
314
+
315
+ Returns:
316
+ Dictionary mapping attribute names to their classes.
317
+
318
+ Example:
319
+ >>> attrs = list_attributes()
320
+ >>> print(attrs.keys())
321
+ dict_keys(['first_name', 'last_name', 'birth_date', 'sex', 'postal_code', 'ssn', 'record_id'])
322
+ """
323
+ return ATTRIBUTE_MAP.copy()
324
+
325
+
326
+ def expression_help() -> str:
327
+ """
328
+ Get help text about expression syntax.
329
+
330
+ Returns:
331
+ A string describing the expression syntax.
332
+ """
333
+ return """
334
+ Expression Syntax Guide:
335
+ ========================
336
+
337
+ Components (separated by |):
338
+ - T : Trim whitespace
339
+ - U : Convert to uppercase
340
+ - S(start,end): Substring (e.g., S(0,3) for first 3 characters)
341
+ - D : Format as date (yyyy-MM-dd)
342
+ - M(regex) : Match regular expression
343
+ - R(old,new) : Replace string
344
+
345
+ Examples:
346
+ - "T|U" : Trim and uppercase (e.g., " john " → "JOHN")
347
+ - "T|S(0,3)|U" : Trim, take first 3 chars, uppercase (e.g., "john" → "JOH")
348
+ - "T|D" : Trim and format as date (e.g., "1990-01-15" → "1990-01-15")
349
+ - "T|S(0,5)" : Trim and take first 5 chars (e.g., "98101-1234" → "98101")
350
+
351
+ Common Patterns:
352
+ - Names: "T|U" or "T|S(0,3)|U"
353
+ - Dates: "T|D"
354
+ - Postal codes: "T|S(0,3)" or "T|S(0,5)"
355
+ - Gender/Sex: "T|U"
356
+ """