tsrkit-types 0.1.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,750 @@
1
+ Metadata-Version: 2.4
2
+ Name: tsrkit-types
3
+ Version: 0.1.0
4
+ Summary: High-performance Python library for type-safe binary serialization, JSON encoding, and data validation with zero dependencies
5
+ Author-email: chainscore-labs <hello@chainscore.finance>, prasad-kumkar <prasad@chainscore.finance>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/chainscore/tsrkit-types
8
+ Project-URL: Repository, https://github.com/chainscore/tsrkit-types
9
+ Project-URL: Issues, https://github.com/chainscore/tsrkit-types/issues
10
+ Project-URL: Documentation, https://github.com/chainscore/tsrkit-types#readme
11
+ Project-URL: Changelog, https://github.com/chainscore/tsrkit-types/blob/main/CHANGELOG.md
12
+ Keywords: serialization,binary,encoding,types,codable,json,validation,data-types,type-safety,zero-copy,protocol,struct,networking,performance,bytes,integers,strings
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3 :: Only
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Topic :: Software Development :: Libraries
22
+ Classifier: Topic :: System :: Networking
23
+ Classifier: Topic :: Internet :: WWW/HTTP
24
+ Classifier: Topic :: Database
25
+ Classifier: Topic :: Utilities
26
+ Classifier: Topic :: System :: Archiving
27
+ Classifier: Topic :: Communications
28
+ Classifier: Typing :: Typed
29
+ Requires-Python: >=3.12
30
+ Description-Content-Type: text/markdown
31
+ License-File: LICENSE
32
+ Provides-Extra: dev
33
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
34
+ Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
35
+ Requires-Dist: pytest-mock>=3.10.0; extra == "dev"
36
+ Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"
37
+ Requires-Dist: pytest-timeout>=2.1.0; extra == "dev"
38
+ Requires-Dist: pytest-xdist>=3.0.0; extra == "dev"
39
+ Dynamic: license-file
40
+
41
+ # TSRKit Types
42
+
43
+ Performant Python library for type-safe binary serialization, JSON encoding, and data validation with zero dependencies.
44
+
45
+ Perfect for network protocols, game state serialization, configuration files, and any application requiring efficient, validated data handling.
46
+
47
+ ## Features
48
+
49
+ - **🔒 Type Safe**: Strong typing with runtime validation and type hints
50
+ - **âš¡ High Performance**: Efficient binary encoding with zero-copy operations where possible
51
+ - **📦 Zero Dependencies**: No external runtime dependencies
52
+ - **🔄 Dual Serialization**: Both binary and JSON serialization support
53
+ - **🧩 Generic Support**: Parameterized types for flexible, reusable code
54
+ - **🎯 Memory Efficient**: Minimal overhead, extends built-in Python types
55
+ - **🚀 Easy to Use**: Intuitive API with comprehensive type system
56
+
57
+ ## Installation
58
+
59
+ ```bash
60
+ pip install tsrkit-types
61
+ ```
62
+
63
+ ## Type Categories
64
+
65
+ ### Integer Types
66
+
67
+ #### Unsigned Integers (`Uint`)
68
+
69
+ The `Uint` class provides both fixed-size and variable-size unsigned integers.
70
+
71
+ **Fixed-Size Integers:**
72
+ ```python
73
+ from tsrkit_types.integers import Uint, U8, U16, U32, U64
74
+
75
+ # Pre-defined types
76
+ value = U8(255) # 8-bit unsigned integer (0-255)
77
+ value = U16(65535) # 16-bit unsigned integer (0-65535)
78
+ value = U32(42949) # 32-bit unsigned integer
79
+ value = U64(1844674) # 64-bit unsigned integer
80
+
81
+ # Dynamic size specification
82
+ U128 = Uint[128] # 128-bit unsigned integer
83
+ value = U128(123456789)
84
+
85
+ # Encoding/Decoding
86
+ encoded = value.encode() # Encode to bytes
87
+ decoded = U8.decode(encoded) # Decode from bytes
88
+ size = value.encode_size() # Get encoded size
89
+ ```
90
+
91
+ **Variable-Size General Integers:**
92
+ ```python
93
+ # General integers (supports up to 2^64 - 1 with variable encoding)
94
+ num = Uint(1000) # Variable-length encoding
95
+ encoded = num.encode()
96
+ decoded = Uint.decode(encoded)
97
+
98
+ # Arithmetic operations preserve type
99
+ a = U8(10)
100
+ b = U8(20)
101
+ result = a + b # result is U8(30)
102
+ ```
103
+
104
+ **Encoding Details:**
105
+ - Fixed-size integers use little-endian encoding
106
+ - Variable-size integers use a compact encoding scheme that optimizes for smaller values
107
+ - Values < 2^7 are encoded in 1 byte
108
+ - Larger values use a variable-length prefix encoding
109
+
110
+ ### String Types
111
+
112
+ #### UTF-8 Strings (`String`)
113
+
114
+ ```python
115
+ from tsrkit_types.string import String
116
+
117
+ # Creation
118
+ text = String("Hello, World!")
119
+ text = String("Unicode: 🚀🔥")
120
+
121
+ # Properties
122
+ length = len(text) # Character count
123
+ text_str = str(text) # Convert to Python str
124
+
125
+ # Encoding/Decoding
126
+ encoded = text.encode() # [length][utf8_bytes]
127
+ decoded = String.decode(encoded)
128
+
129
+ # JSON serialization
130
+ json_data = text.to_json() # Returns the string value
131
+ restored = String.from_json(json_data)
132
+ ```
133
+
134
+ **Encoding Format:**
135
+ - Length prefix (variable-length `Uint`) followed by UTF-8 bytes
136
+ - String length is measured in UTF-16 code units (like Python strings)
137
+
138
+ ### Boolean Types
139
+
140
+ #### Boolean (`Bool`)
141
+
142
+ ```python
143
+ from tsrkit_types.bool import Bool
144
+
145
+ # Creation
146
+ true_val = Bool(True)
147
+ false_val = Bool(False)
148
+
149
+ # Usage
150
+ if true_val: # Supports truthiness testing
151
+ print("It's true!")
152
+
153
+ # Encoding/Decoding
154
+ encoded = true_val.encode() # 1 byte: 0x01 or 0x00
155
+ decoded = Bool.decode(encoded)
156
+
157
+ # JSON serialization
158
+ json_str = true_val.to_json() # "true" or "false"
159
+ restored = Bool.from_json("true")
160
+ ```
161
+
162
+ ### Null Types
163
+
164
+ #### Null and Nullable
165
+
166
+ ```python
167
+ from tsrkit_types.null import Null, Nullable
168
+
169
+ # Null type
170
+ null_val = Null # Singleton null value
171
+
172
+ # Nullable wrapper for optional values
173
+ maybe_string = Nullable[String](String("hello"))
174
+ empty_val = Nullable[String]() # Contains null
175
+
176
+ # Check for null
177
+ if maybe_string.is_null():
178
+ print("Value is null")
179
+ else:
180
+ value = maybe_string.unwrap() # Get the wrapped value
181
+
182
+ # Encoding
183
+ encoded = maybe_string.encode() # 1 byte flag + optional value
184
+ decoded = Nullable[String].decode(encoded)
185
+ ```
186
+
187
+ ### Choice and Option Types
188
+
189
+ #### Choice (Union Types)
190
+
191
+ ```python
192
+ from tsrkit_types.choice import Choice
193
+ from tsrkit_types.integers import U8, U16
194
+ from tsrkit_types.string import String
195
+
196
+ # Anonymous choice
197
+ IntOrString = Choice[U8, String]
198
+ value = IntOrString(U8(42))
199
+ value = IntOrString(String("hello"))
200
+
201
+ # Switch the choice
202
+ value.set(String("world"))
203
+ inner = value.unwrap() # Get the inner value
204
+
205
+ # Named choice with custom keys
206
+ class Result(Choice):
207
+ success: String
208
+ error: U8
209
+
210
+ result = Result(String("OK"))
211
+ result = Result(U8(404), key="error")
212
+
213
+ # JSON serialization
214
+ json_data = result.to_json() # {"success": "OK"} or {"error": 404}
215
+ restored = Result.from_json(json_data)
216
+
217
+ # Encoding
218
+ encoded = result.encode() # [variant_index][value]
219
+ decoded = Result.decode(encoded)
220
+ ```
221
+
222
+ #### Option (Optional Values)
223
+
224
+ ```python
225
+ from tsrkit_types.option import Option
226
+ from tsrkit_types.integers import U32
227
+
228
+ # Optional value
229
+ opt_val = Option[U32](U32(100)) # Some value
230
+ empty_opt = Option[U32]() # None/Null
231
+
232
+ # Check if has value
233
+ if opt_val: # Truthiness test
234
+ value = opt_val.unwrap() # Get the U32 value
235
+
236
+ # Encoding (more efficient than general Choice)
237
+ encoded = opt_val.encode() # 1 byte tag + optional value
238
+ decoded = Option[U32].decode(encoded)
239
+ ```
240
+
241
+ ### Container Types
242
+
243
+ #### Sequences (Arrays and Vectors)
244
+
245
+ ```python
246
+ from tsrkit_types.sequences import Array, Vector, TypedArray, TypedVector
247
+ from tsrkit_types.integers import U16
248
+
249
+ # Fixed-size array
250
+ FixedArray = Array[10] # Array of exactly 10 elements
251
+ arr = FixedArray([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
252
+
253
+ # Typed fixed-size array
254
+ TypedFixedArray = TypedArray[U16, 5] # 5 U16 elements
255
+ typed_arr = TypedFixedArray([U16(1), U16(2), U16(3), U16(4), U16(5)])
256
+
257
+ # Variable-size vector
258
+ DynamicVector = Vector[100] # Vector with max 100 elements
259
+ vec = DynamicVector([1, 2, 3])
260
+
261
+ # Typed variable-size vector
262
+ TypedDynamicVector = TypedVector[U16] # Vector of U16 elements
263
+ typed_vec = TypedDynamicVector([U16(1), U16(2)])
264
+
265
+ # Operations
266
+ vec.append(4) # Add element with validation
267
+ vec.extend([5, 6, 7]) # Add multiple elements
268
+ vec[0] = 100 # Set element with validation
269
+
270
+ # Encoding
271
+ encoded = typed_vec.encode() # [length?][element1][element2]...
272
+ decoded = TypedDynamicVector.decode(encoded)
273
+ ```
274
+
275
+ **Sequence Types:**
276
+ - `Array[N]`: Fixed size, any element type
277
+ - `Vector[max]`: Variable size up to max, any element type
278
+ - `TypedArray[T, N]`: Fixed size with typed elements
279
+ - `TypedVector[T]`: Variable size with typed elements
280
+ - `BoundedVector[min, max]`: Size constrained vector
281
+ - `TypedBoundedVector[T, min, max]`: Typed and size constrained
282
+
283
+ #### Dictionary
284
+
285
+ ```python
286
+ from tsrkit_types.dictionary import Dictionary
287
+ from tsrkit_types.string import String
288
+ from tsrkit_types.integers import U32
289
+
290
+ # Create dictionary type
291
+ StringToInt = Dictionary[String, U32]
292
+ data = StringToInt({
293
+ String("key1"): U32(100),
294
+ String("key2"): U32(200)
295
+ })
296
+
297
+ # Operations
298
+ data[String("key3")] = U32(300) # Add entry
299
+ value = data[String("key1")] # Get value
300
+ del data[String("key2")] # Remove entry
301
+
302
+ # Iteration
303
+ for key, value in data.items():
304
+ print(f"{key}: {value}")
305
+
306
+ # Encoding
307
+ encoded = data.encode() # [length][key1][value1][key2][value2]...
308
+ decoded = StringToInt.decode(encoded)
309
+
310
+ # JSON serialization
311
+ json_data = data.to_json() # {"key1": 100, "key3": 300}
312
+ restored = StringToInt.from_json(json_data)
313
+ ```
314
+
315
+ ### Bytes Types
316
+
317
+ #### Raw Bytes
318
+
319
+ ```python
320
+ from tsrkit_types.bytes import Bytes
321
+
322
+ # Creation
323
+ data = Bytes(b"Hello, binary world!")
324
+ data = Bytes([0x01, 0x02, 0x03, 0x04])
325
+
326
+ # Operations
327
+ length = len(data) # Byte length
328
+ raw_bytes = bytes(data) # Convert to Python bytes
329
+
330
+ # Encoding
331
+ encoded = data.encode() # [length][raw_bytes]
332
+ decoded = Bytes.decode(encoded)
333
+
334
+ # JSON serialization (hex encoded)
335
+ json_str = data.to_json() # "48656c6c6f2c2062696e61727920776f726c6421"
336
+ restored = Bytes.from_json(json_str)
337
+ ```
338
+
339
+ #### Bit Arrays
340
+
341
+ ```python
342
+ from tsrkit_types.bits import BitArray
343
+
344
+ # Creation
345
+ bits = BitArray([True, False, True, True, False])
346
+ bits = BitArray.from_hex("1A3F") # From hex string
347
+ bits = BitArray.from_int(42, 8) # From integer with bit width
348
+
349
+ # Operations
350
+ bit_val = bits[0] # Get bit at index
351
+ bits[1] = True # Set bit at index
352
+ bits.append(False) # Add bit
353
+ bits.extend([True, False]) # Add multiple bits
354
+
355
+ # Conversion
356
+ hex_str = bits.to_hex() # Convert to hex string
357
+ int_val = bits.to_int() # Convert to integer
358
+
359
+ # Encoding
360
+ encoded = bits.encode() # [length][packed_bits]
361
+ decoded = BitArray.decode(encoded)
362
+ ```
363
+
364
+ ### Enumeration Types
365
+
366
+ ```python
367
+ from tsrkit_types.enum import Enum
368
+
369
+ # Define enum
370
+ class Color(Enum):
371
+ RED = 0
372
+ GREEN = 1
373
+ BLUE = 2
374
+
375
+ # Usage
376
+ color = Color.RED
377
+ color_val = Color(1) # Color.GREEN
378
+
379
+ # String conversion
380
+ name = color.name # "RED"
381
+ value = color.value # 0
382
+
383
+ # Encoding
384
+ encoded = color.encode() # Variable-length uint encoding
385
+ decoded = Color.decode(encoded)
386
+
387
+ # JSON serialization
388
+ json_data = color.to_json() # "RED"
389
+ restored = Color.from_json("GREEN")
390
+ ```
391
+
392
+ ### Structured Types
393
+
394
+ #### Struct Decorator
395
+
396
+ ```python
397
+ from tsrkit_types.struct import struct
398
+ from tsrkit_types.string import String
399
+ from tsrkit_types.integers import U8, U32
400
+ from dataclasses import field
401
+
402
+ @struct
403
+ class Person:
404
+ name: String
405
+ age: U8
406
+
407
+ @struct
408
+ class Employee:
409
+ person: Person
410
+ employee_id: U32
411
+ department: String = field(metadata={"default": String("Unknown")})
412
+
413
+ # Creation
414
+ person = Person(name=String("John Doe"), age=U8(30))
415
+ employee = Employee(
416
+ person=person,
417
+ employee_id=U32(12345),
418
+ department=String("Engineering")
419
+ )
420
+
421
+ # Access fields
422
+ print(employee.person.name) # "John Doe"
423
+ print(employee.employee_id) # 12345
424
+
425
+ # Encoding/Decoding
426
+ encoded = employee.encode() # Concatenated field encodings
427
+ decoded = Employee.decode(encoded)
428
+
429
+ # JSON serialization with custom field names
430
+ @struct
431
+ class CustomPerson:
432
+ name: String = field(metadata={"name": "full_name"})
433
+ age: U8
434
+
435
+ person = CustomPerson(name=String("Jane"), age=U8(25))
436
+ json_data = person.to_json() # {"full_name": "Jane", "age": 25}
437
+ restored = CustomPerson.from_json({"full_name": "Jane", "age": 25})
438
+ ```
439
+
440
+ **Struct Features:**
441
+ - Automatic `Codable` implementation
442
+ - Field validation and type checking
443
+ - Default values via metadata
444
+ - Custom JSON field mapping
445
+ - Inheritance support
446
+ - Frozen/immutable variants
447
+
448
+ ## Advanced Usage
449
+
450
+ ### Custom Types
451
+
452
+ Implement your own `Codable` types:
453
+
454
+ ```python
455
+ from tsrkit_types.itf.codable import Codable
456
+ from typing import Tuple, Union
457
+
458
+ class Point3D(Codable):
459
+ def __init__(self, x: float, y: float, z: float):
460
+ self.x, self.y, self.z = x, y, z
461
+
462
+ def encode_size(self) -> int:
463
+ return 24 # 3 doubles = 24 bytes
464
+
465
+ def encode_into(self, buffer: bytearray, offset: int = 0) -> int:
466
+ import struct
467
+ struct.pack_into('<ddd', buffer, offset, self.x, self.y, self.z)
468
+ return 24
469
+
470
+ @classmethod
471
+ def decode_from(cls, buffer: Union[bytes, bytearray, memoryview],
472
+ offset: int = 0) -> Tuple['Point3D', int]:
473
+ import struct
474
+ x, y, z = struct.unpack_from('<ddd', buffer, offset)
475
+ return cls(x, y, z), 24
476
+ ```
477
+
478
+ ### Configuration and Optimization
479
+
480
+ ```python
481
+ # Optimize for specific use cases
482
+ @struct(frozen=True) # Immutable structs
483
+ class ImmutableData:
484
+ value: U64
485
+
486
+ # Memory-efficient sequences
487
+ CompactArray = TypedArray[U8, 1000] # 1000 bytes exactly
488
+ data = CompactArray([0] * 1000)
489
+
490
+ # Bounded containers for validation
491
+ BoundedList = BoundedVector[10, 100] # Between 10 and 100 elements
492
+ safe_list = BoundedList([0] * 50)
493
+ ```
494
+
495
+ ### Error Handling
496
+
497
+ ```python
498
+ from tsrkit_types.integers import U8
499
+
500
+ try:
501
+ # Value out of range
502
+ invalid = U8(256) # Raises ValueError
503
+ except ValueError as e:
504
+ print(f"Range error: {e}")
505
+
506
+ try:
507
+ # Type mismatch in Choice
508
+ choice = Choice[U8, String](42) # Raises TypeError
509
+ except TypeError as e:
510
+ print(f"Type error: {e}")
511
+
512
+ try:
513
+ # Buffer too small for decoding
514
+ corrupted = b"\x01"
515
+ U32.decode(corrupted) # Raises ValueError
516
+ except ValueError as e:
517
+ print(f"Decode error: {e}")
518
+ ```
519
+
520
+
521
+ ## Core Interface
522
+
523
+ All types implement the `Codable` interface:
524
+
525
+ ```python
526
+ from tsrkit_types.itf.codable import Codable
527
+
528
+ class MyType(Codable):
529
+ def encode_size(self) -> int: ... # Size needed for encoding
530
+ def encode_into(self, buffer: bytearray, offset: int = 0) -> int: ... # Encode into buffer
531
+ def encode(self) -> bytes: ... # Encode to new bytes object
532
+
533
+ @classmethod
534
+ def decode_from(cls, buffer: bytes, offset: int = 0) -> Tuple[T, int]: ... # Decode from buffer
535
+ @classmethod
536
+ def decode(cls, buffer: bytes, offset: int = 0) -> T: ... # Decode from buffer (convenience)
537
+ ```
538
+
539
+ ## Performance Considerations
540
+
541
+ ### Encoding Efficiency
542
+
543
+ - **Fixed-size types** (U8, U16, etc.) have constant encoding size
544
+ - **Variable-size types** (general Uint) optimize for smaller values
545
+ - **Sequences** encode length only when necessary (variable-size)
546
+ - **Strings** use UTF-8 encoding with variable-length prefix
547
+
548
+ ### Memory Usage
549
+
550
+ - Types extend built-in Python types where possible (int, str, list, dict)
551
+ - Zero-copy operations where feasible
552
+ - Minimal overhead for type metadata
553
+
554
+ ### Best Practices
555
+
556
+ ```python
557
+ # Prefer fixed-size types when range is known
558
+ user_id = U32(123456) # Better than Uint(123456)
559
+
560
+ # Use typed containers for homogeneous data
561
+ scores = TypedVector[U16]([100, 95, 87, 92])
562
+
563
+ # Batch operations for better performance
564
+ data = TypedArray[U8, 1000]([0] * 1000)
565
+ encoded = data.encode() # Single operation vs. encoding each element
566
+
567
+ # Reuse buffer for multiple encodings
568
+ buffer = bytearray(1024)
569
+ offset = 0
570
+ offset += value1.encode_into(buffer, offset)
571
+ offset += value2.encode_into(buffer, offset)
572
+ ```
573
+
574
+ ## Examples
575
+
576
+ ### Network Protocol
577
+
578
+ ```python
579
+ @struct
580
+ class NetworkPacket:
581
+ packet_type: U8
582
+ session_id: U32
583
+ payload_length: U16
584
+ payload: Bytes
585
+
586
+ # Create packet
587
+ packet = NetworkPacket(
588
+ packet_type=U8(1),
589
+ session_id=U32(0x12345678),
590
+ payload_length=U16(13),
591
+ payload=Bytes(b"Hello, World!")
592
+ )
593
+
594
+ # Serialize for transmission
595
+ wire_data = packet.encode()
596
+
597
+ # Deserialize on receiver
598
+ received_packet = NetworkPacket.decode(wire_data)
599
+ ```
600
+
601
+ ### Configuration File
602
+
603
+ ```python
604
+ @struct
605
+ class DatabaseConfig:
606
+ host: String
607
+ port: U16
608
+ username: String
609
+ password: String
610
+ max_connections: U8 = field(metadata={"default": U8(10)})
611
+ ssl_enabled: Bool = field(metadata={"default": Bool(True)})
612
+
613
+ # Create config
614
+ config = DatabaseConfig(
615
+ host=String("localhost"),
616
+ port=U16(5432),
617
+ username=String("admin"),
618
+ password=String("secret")
619
+ )
620
+
621
+ # Save to JSON
622
+ import json
623
+ with open("db_config.json", "w") as f:
624
+ json.dump(config.to_json(), f)
625
+
626
+ # Load from JSON
627
+ with open("db_config.json", "r") as f:
628
+ data = json.load(f)
629
+ config = DatabaseConfig.from_json(data)
630
+ ```
631
+
632
+ ### Game State Serialization
633
+
634
+ ```python
635
+ class GameEntityType(Enum):
636
+ PLAYER = 0
637
+ ENEMY = 1
638
+ ITEM = 2
639
+
640
+ @struct
641
+ class Position:
642
+ x: U16
643
+ y: U16
644
+
645
+ @struct
646
+ class GameEntity:
647
+ entity_type: GameEntityType
648
+ position: Position
649
+ health: U8
650
+ name: String
651
+
652
+ @struct
653
+ class GameState:
654
+ level: U8
655
+ score: U32
656
+ entities: TypedVector[GameEntity]
657
+
658
+ # Create game state
659
+ state = GameState(
660
+ level=U8(1),
661
+ score=U32(1500),
662
+ entities=TypedVector[GameEntity]([
663
+ GameEntity(
664
+ entity_type=GameEntityType.PLAYER,
665
+ position=Position(x=U16(100), y=U16(200)),
666
+ health=U8(100),
667
+ name=String("Hero")
668
+ )
669
+ ])
670
+ )
671
+
672
+ # Save/load game
673
+ save_data = state.encode()
674
+ loaded_state = GameState.decode(save_data)
675
+ ```
676
+
677
+ ## Development
678
+
679
+ ### Setup
680
+
681
+ 1. **Clone the repository:**
682
+ ```bash
683
+ git clone https://github.com/chainscore/tsrkit-types.git
684
+ cd tsrkit-types
685
+ ```
686
+
687
+ 2. **Install development dependencies:**
688
+ ```bash
689
+ pip install -e ".[dev]"
690
+ ```
691
+
692
+ ### Running Tests
693
+
694
+ **Run the full test suite:**
695
+ ```bash
696
+ pytest
697
+ ```
698
+
699
+ **Run tests with coverage:**
700
+ ```bash
701
+ pytest --cov=tsrkit_types --cov-report=html
702
+ ```
703
+
704
+ **Run tests in parallel (faster):**
705
+ ```bash
706
+ pytest -n auto
707
+ ```
708
+
709
+ **Run specific test categories:**
710
+ ```bash
711
+ pytest tests/test_integers.py # Integer type tests
712
+ pytest tests/test_strings.py # String type tests
713
+ pytest tests/test_containers.py # Container type tests
714
+ pytest tests/test_structs.py # Struct tests
715
+ pytest tests/test_network.py # Network protocol tests
716
+ ```
717
+
718
+ **Run tests with verbose output:**
719
+ ```bash
720
+ pytest -v
721
+ ```
722
+
723
+ **Skip slow tests:**
724
+ ```bash
725
+ pytest -m "not slow"
726
+ ```
727
+
728
+ ### Test Coverage
729
+
730
+ View the test coverage report:
731
+ ```bash
732
+ # Generate HTML coverage report
733
+ pytest --cov=tsrkit_types --cov-report=html
734
+ open htmlcov/index.html # macOS
735
+ # or xdg-open htmlcov/index.html # Linux
736
+ ```
737
+
738
+ ## License
739
+
740
+ MIT License - see [LICENSE](LICENSE) file for details.
741
+
742
+ ## Contributing
743
+
744
+ Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines.
745
+
746
+ ## Requirements
747
+
748
+ - **Python**: >= 3.12
749
+ - **Runtime Dependencies**: None (zero dependencies!)
750
+ - **Development Dependencies**: pytest and plugins (see `pyproject.toml`)