exonware-xwsystem 0.1.0.1__py3-none-any.whl → 0.1.0.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.
- exonware/__init__.py +2 -1
- exonware/conf.py +2 -2
- exonware/xwsystem/__init__.py +115 -43
- exonware/xwsystem/base.py +30 -0
- exonware/xwsystem/caching/__init__.py +39 -13
- exonware/xwsystem/caching/base.py +24 -6
- exonware/xwsystem/caching/bloom_cache.py +2 -2
- exonware/xwsystem/caching/cache_manager.py +2 -1
- exonware/xwsystem/caching/conditional.py +2 -2
- exonware/xwsystem/caching/contracts.py +85 -139
- exonware/xwsystem/caching/decorators.py +6 -19
- exonware/xwsystem/caching/defs.py +2 -1
- exonware/xwsystem/caching/disk_cache.py +2 -1
- exonware/xwsystem/caching/distributed.py +2 -1
- exonware/xwsystem/caching/errors.py +2 -1
- exonware/xwsystem/caching/events.py +110 -27
- exonware/xwsystem/caching/eviction_strategies.py +2 -2
- exonware/xwsystem/caching/external_caching_python.py +701 -0
- exonware/xwsystem/caching/facade.py +253 -0
- exonware/xwsystem/caching/factory.py +300 -0
- exonware/xwsystem/caching/fluent.py +14 -12
- exonware/xwsystem/caching/integrity.py +21 -6
- exonware/xwsystem/caching/lfu_cache.py +2 -1
- exonware/xwsystem/caching/lfu_optimized.py +18 -6
- exonware/xwsystem/caching/lru_cache.py +7 -4
- exonware/xwsystem/caching/memory_bounded.py +2 -2
- exonware/xwsystem/caching/metrics_exporter.py +2 -2
- exonware/xwsystem/caching/observable_cache.py +2 -2
- exonware/xwsystem/caching/pluggable_cache.py +2 -2
- exonware/xwsystem/caching/rate_limiter.py +2 -2
- exonware/xwsystem/caching/read_through.py +2 -2
- exonware/xwsystem/caching/secure_cache.py +81 -28
- exonware/xwsystem/caching/serializable.py +9 -7
- exonware/xwsystem/caching/stats.py +2 -2
- exonware/xwsystem/caching/tagging.py +2 -2
- exonware/xwsystem/caching/ttl_cache.py +4 -3
- exonware/xwsystem/caching/two_tier_cache.py +6 -3
- exonware/xwsystem/caching/utils.py +30 -12
- exonware/xwsystem/caching/validation.py +2 -2
- exonware/xwsystem/caching/warming.py +6 -3
- exonware/xwsystem/caching/write_behind.py +15 -6
- exonware/xwsystem/config/__init__.py +11 -17
- exonware/xwsystem/config/base.py +5 -5
- exonware/xwsystem/config/contracts.py +93 -153
- exonware/xwsystem/config/defaults.py +3 -2
- exonware/xwsystem/config/defs.py +3 -2
- exonware/xwsystem/config/errors.py +2 -5
- exonware/xwsystem/config/logging.py +12 -8
- exonware/xwsystem/config/logging_setup.py +3 -2
- exonware/xwsystem/config/performance.py +1 -46
- exonware/xwsystem/config/performance_modes.py +9 -8
- exonware/xwsystem/config/version_manager.py +1 -0
- exonware/xwsystem/config.py +27 -0
- exonware/xwsystem/console/__init__.py +53 -0
- exonware/xwsystem/console/base.py +133 -0
- exonware/xwsystem/console/cli/__init__.py +61 -0
- exonware/xwsystem/{cli → console/cli}/args.py +27 -24
- exonware/xwsystem/{cli → console/cli}/base.py +18 -87
- exonware/xwsystem/{cli → console/cli}/colors.py +15 -13
- exonware/xwsystem/console/cli/console.py +98 -0
- exonware/xwsystem/{cli → console/cli}/contracts.py +51 -69
- exonware/xwsystem/console/cli/defs.py +87 -0
- exonware/xwsystem/console/cli/encoding.py +69 -0
- exonware/xwsystem/{cli → console/cli}/errors.py +8 -3
- exonware/xwsystem/console/cli/event_logger.py +166 -0
- exonware/xwsystem/{cli → console/cli}/progress.py +25 -21
- exonware/xwsystem/{cli → console/cli}/prompts.py +3 -2
- exonware/xwsystem/{cli → console/cli}/tables.py +27 -24
- exonware/xwsystem/console/contracts.py +113 -0
- exonware/xwsystem/console/defs.py +154 -0
- exonware/xwsystem/console/errors.py +34 -0
- exonware/xwsystem/console/event_logger.py +385 -0
- exonware/xwsystem/console/writer.py +132 -0
- exonware/xwsystem/contracts.py +28 -0
- exonware/xwsystem/data_structures/__init__.py +23 -0
- exonware/xwsystem/data_structures/trie.py +34 -0
- exonware/xwsystem/data_structures/union_find.py +144 -0
- exonware/xwsystem/defs.py +17 -0
- exonware/xwsystem/errors.py +23 -0
- exonware/xwsystem/facade.py +62 -0
- exonware/xwsystem/http_client/__init__.py +22 -1
- exonware/xwsystem/http_client/advanced_client.py +8 -5
- exonware/xwsystem/http_client/base.py +3 -2
- exonware/xwsystem/http_client/client.py +7 -4
- exonware/xwsystem/http_client/contracts.py +42 -56
- exonware/xwsystem/http_client/defs.py +2 -1
- exonware/xwsystem/http_client/errors.py +2 -1
- exonware/xwsystem/http_client/facade.py +156 -0
- exonware/xwsystem/io/__init__.py +22 -3
- exonware/xwsystem/io/archive/__init__.py +8 -2
- exonware/xwsystem/io/archive/archive.py +1 -1
- exonware/xwsystem/io/archive/archive_files.py +4 -7
- exonware/xwsystem/io/archive/archivers.py +120 -10
- exonware/xwsystem/io/archive/base.py +4 -5
- exonware/xwsystem/io/archive/codec_integration.py +1 -2
- exonware/xwsystem/io/archive/compression.py +1 -2
- exonware/xwsystem/io/archive/facade.py +263 -0
- exonware/xwsystem/io/archive/formats/__init__.py +2 -3
- exonware/xwsystem/io/archive/formats/brotli_format.py +20 -7
- exonware/xwsystem/io/archive/formats/lz4_format.py +20 -7
- exonware/xwsystem/io/archive/formats/rar.py +11 -5
- exonware/xwsystem/io/archive/formats/sevenzip.py +12 -6
- exonware/xwsystem/io/archive/formats/squashfs_format.py +1 -2
- exonware/xwsystem/io/archive/formats/tar.py +52 -7
- exonware/xwsystem/io/archive/formats/wim_format.py +11 -5
- exonware/xwsystem/io/archive/formats/zip.py +1 -2
- exonware/xwsystem/io/archive/formats/zpaq_format.py +1 -2
- exonware/xwsystem/io/archive/formats/zstandard.py +20 -7
- exonware/xwsystem/io/base.py +119 -115
- exonware/xwsystem/io/codec/__init__.py +4 -2
- exonware/xwsystem/io/codec/base.py +19 -13
- exonware/xwsystem/io/codec/contracts.py +59 -2
- exonware/xwsystem/io/codec/registry.py +67 -21
- exonware/xwsystem/io/common/__init__.py +1 -1
- exonware/xwsystem/io/common/atomic.py +29 -16
- exonware/xwsystem/io/common/base.py +11 -10
- exonware/xwsystem/io/common/lock.py +6 -5
- exonware/xwsystem/io/common/path_manager.py +2 -1
- exonware/xwsystem/io/common/watcher.py +1 -2
- exonware/xwsystem/io/contracts.py +301 -433
- exonware/xwsystem/io/contracts_1.py +1180 -0
- exonware/xwsystem/io/data_operations.py +19 -20
- exonware/xwsystem/io/defs.py +4 -3
- exonware/xwsystem/io/errors.py +3 -2
- exonware/xwsystem/io/facade.py +87 -61
- exonware/xwsystem/io/file/__init__.py +1 -1
- exonware/xwsystem/io/file/base.py +8 -9
- exonware/xwsystem/io/file/conversion.py +2 -3
- exonware/xwsystem/io/file/file.py +61 -18
- exonware/xwsystem/io/file/paged_source.py +8 -8
- exonware/xwsystem/io/file/paging/__init__.py +1 -2
- exonware/xwsystem/io/file/paging/byte_paging.py +4 -5
- exonware/xwsystem/io/file/paging/line_paging.py +2 -3
- exonware/xwsystem/io/file/paging/record_paging.py +2 -3
- exonware/xwsystem/io/file/paging/registry.py +1 -2
- exonware/xwsystem/io/file/source.py +13 -17
- exonware/xwsystem/io/filesystem/__init__.py +1 -1
- exonware/xwsystem/io/filesystem/base.py +1 -2
- exonware/xwsystem/io/filesystem/local.py +3 -4
- exonware/xwsystem/io/folder/__init__.py +1 -1
- exonware/xwsystem/io/folder/base.py +1 -2
- exonware/xwsystem/io/folder/folder.py +16 -7
- exonware/xwsystem/io/indexing/__init__.py +14 -0
- exonware/xwsystem/io/indexing/facade.py +443 -0
- exonware/xwsystem/io/path_parser.py +98 -0
- exonware/xwsystem/io/serialization/__init__.py +21 -3
- exonware/xwsystem/io/serialization/auto_serializer.py +146 -20
- exonware/xwsystem/io/serialization/base.py +84 -34
- exonware/xwsystem/io/serialization/contracts.py +50 -73
- exonware/xwsystem/io/serialization/defs.py +2 -1
- exonware/xwsystem/io/serialization/errors.py +2 -1
- exonware/xwsystem/io/serialization/flyweight.py +154 -7
- exonware/xwsystem/io/serialization/format_detector.py +15 -14
- exonware/xwsystem/io/serialization/formats/__init__.py +8 -5
- exonware/xwsystem/io/serialization/formats/binary/bson.py +15 -6
- exonware/xwsystem/io/serialization/formats/binary/cbor.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/marshal.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/msgpack.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/pickle.py +5 -5
- exonware/xwsystem/io/serialization/formats/binary/plistlib.py +5 -5
- exonware/xwsystem/io/serialization/formats/database/dbm.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/shelve.py +7 -7
- exonware/xwsystem/io/serialization/formats/database/sqlite3.py +7 -7
- exonware/xwsystem/io/serialization/formats/tabular/__init__.py +27 -0
- exonware/xwsystem/io/serialization/formats/tabular/base.py +89 -0
- exonware/xwsystem/io/serialization/formats/tabular/csv.py +319 -0
- exonware/xwsystem/io/serialization/formats/tabular/df.py +249 -0
- exonware/xwsystem/io/serialization/formats/tabular/excel.py +291 -0
- exonware/xwsystem/io/serialization/formats/tabular/googlesheets.py +374 -0
- exonware/xwsystem/io/serialization/formats/text/__init__.py +1 -1
- exonware/xwsystem/io/serialization/formats/text/append_only_log.py +5 -7
- exonware/xwsystem/io/serialization/formats/text/configparser.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/csv.py +7 -5
- exonware/xwsystem/io/serialization/formats/text/formdata.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/json.py +27 -18
- exonware/xwsystem/io/serialization/formats/text/json5.py +8 -4
- exonware/xwsystem/io/serialization/formats/text/jsonlines.py +18 -14
- exonware/xwsystem/io/serialization/formats/text/multipart.py +5 -5
- exonware/xwsystem/io/serialization/formats/text/toml.py +8 -6
- exonware/xwsystem/io/serialization/formats/text/xml.py +25 -20
- exonware/xwsystem/io/serialization/formats/text/yaml.py +8 -6
- exonware/xwsystem/io/serialization/parsers/__init__.py +3 -2
- exonware/xwsystem/io/serialization/parsers/base.py +6 -5
- exonware/xwsystem/io/serialization/parsers/hybrid_parser.py +7 -6
- exonware/xwsystem/io/serialization/parsers/msgspec_parser.py +10 -7
- exonware/xwsystem/io/serialization/parsers/orjson_direct_parser.py +7 -6
- exonware/xwsystem/io/serialization/parsers/orjson_parser.py +11 -8
- exonware/xwsystem/io/serialization/parsers/pysimdjson_parser.py +13 -9
- exonware/xwsystem/io/serialization/parsers/rapidjson_parser.py +10 -7
- exonware/xwsystem/io/serialization/parsers/registry.py +11 -10
- exonware/xwsystem/io/serialization/parsers/standard.py +7 -6
- exonware/xwsystem/io/serialization/parsers/ujson_parser.py +10 -7
- exonware/xwsystem/io/serialization/registry.py +4 -4
- exonware/xwsystem/io/serialization/serializer.py +168 -79
- exonware/xwsystem/io/serialization/universal_options.py +367 -0
- exonware/xwsystem/io/serialization/utils/__init__.py +1 -2
- exonware/xwsystem/io/serialization/utils/path_ops.py +5 -6
- exonware/xwsystem/io/source_reader.py +223 -0
- exonware/xwsystem/io/stream/__init__.py +1 -1
- exonware/xwsystem/io/stream/async_operations.py +61 -14
- exonware/xwsystem/io/stream/base.py +1 -2
- exonware/xwsystem/io/stream/codec_io.py +6 -7
- exonware/xwsystem/ipc/__init__.py +1 -0
- exonware/xwsystem/ipc/async_fabric.py +4 -4
- exonware/xwsystem/ipc/base.py +6 -5
- exonware/xwsystem/ipc/contracts.py +41 -66
- exonware/xwsystem/ipc/defs.py +2 -1
- exonware/xwsystem/ipc/errors.py +2 -1
- exonware/xwsystem/ipc/message_queue.py +5 -2
- exonware/xwsystem/ipc/pipes.py +70 -34
- exonware/xwsystem/ipc/process_manager.py +7 -5
- exonware/xwsystem/ipc/process_pool.py +6 -5
- exonware/xwsystem/ipc/shared_memory.py +64 -11
- exonware/xwsystem/monitoring/__init__.py +7 -0
- exonware/xwsystem/monitoring/base.py +11 -8
- exonware/xwsystem/monitoring/contracts.py +86 -144
- exonware/xwsystem/monitoring/defs.py +2 -1
- exonware/xwsystem/monitoring/error_recovery.py +16 -3
- exonware/xwsystem/monitoring/errors.py +2 -1
- exonware/xwsystem/monitoring/facade.py +183 -0
- exonware/xwsystem/monitoring/memory_monitor.py +1 -0
- exonware/xwsystem/monitoring/metrics.py +1 -0
- exonware/xwsystem/monitoring/performance_manager_generic.py +7 -7
- exonware/xwsystem/monitoring/performance_monitor.py +1 -0
- exonware/xwsystem/monitoring/performance_validator.py +1 -0
- exonware/xwsystem/monitoring/system_monitor.py +6 -5
- exonware/xwsystem/monitoring/tracing.py +18 -16
- exonware/xwsystem/monitoring/tracker.py +2 -1
- exonware/xwsystem/operations/__init__.py +5 -50
- exonware/xwsystem/operations/base.py +3 -44
- exonware/xwsystem/operations/contracts.py +25 -15
- exonware/xwsystem/operations/defs.py +1 -1
- exonware/xwsystem/operations/diff.py +5 -4
- exonware/xwsystem/operations/errors.py +1 -1
- exonware/xwsystem/operations/merge.py +6 -4
- exonware/xwsystem/operations/patch.py +5 -4
- exonware/xwsystem/patterns/__init__.py +1 -0
- exonware/xwsystem/patterns/base.py +2 -1
- exonware/xwsystem/patterns/context_manager.py +2 -1
- exonware/xwsystem/patterns/contracts.py +215 -256
- exonware/xwsystem/patterns/defs.py +2 -1
- exonware/xwsystem/patterns/dynamic_facade.py +1 -0
- exonware/xwsystem/patterns/errors.py +2 -4
- exonware/xwsystem/patterns/handler_factory.py +2 -3
- exonware/xwsystem/patterns/import_registry.py +1 -0
- exonware/xwsystem/patterns/object_pool.py +1 -0
- exonware/xwsystem/patterns/registry.py +4 -43
- exonware/xwsystem/plugins/__init__.py +2 -1
- exonware/xwsystem/plugins/base.py +6 -5
- exonware/xwsystem/plugins/contracts.py +94 -158
- exonware/xwsystem/plugins/defs.py +2 -1
- exonware/xwsystem/plugins/errors.py +2 -1
- exonware/xwsystem/py.typed +3 -0
- exonware/xwsystem/query/__init__.py +36 -0
- exonware/xwsystem/query/contracts.py +56 -0
- exonware/xwsystem/query/errors.py +22 -0
- exonware/xwsystem/query/registry.py +128 -0
- exonware/xwsystem/runtime/__init__.py +2 -1
- exonware/xwsystem/runtime/base.py +4 -3
- exonware/xwsystem/runtime/contracts.py +39 -60
- exonware/xwsystem/runtime/defs.py +2 -1
- exonware/xwsystem/runtime/env.py +11 -9
- exonware/xwsystem/runtime/errors.py +2 -1
- exonware/xwsystem/runtime/reflection.py +3 -2
- exonware/xwsystem/security/__init__.py +68 -11
- exonware/xwsystem/security/audit.py +167 -0
- exonware/xwsystem/security/base.py +121 -24
- exonware/xwsystem/security/contracts.py +91 -146
- exonware/xwsystem/security/crypto.py +17 -16
- exonware/xwsystem/security/defs.py +2 -1
- exonware/xwsystem/security/errors.py +2 -1
- exonware/xwsystem/security/facade.py +321 -0
- exonware/xwsystem/security/file_security.py +330 -0
- exonware/xwsystem/security/hazmat.py +11 -8
- exonware/xwsystem/security/monitor.py +372 -0
- exonware/xwsystem/security/path_validator.py +140 -18
- exonware/xwsystem/security/policy.py +357 -0
- exonware/xwsystem/security/resource_limits.py +1 -0
- exonware/xwsystem/security/validator.py +455 -0
- exonware/xwsystem/shared/__init__.py +14 -1
- exonware/xwsystem/shared/base.py +285 -2
- exonware/xwsystem/shared/contracts.py +415 -126
- exonware/xwsystem/shared/defs.py +2 -1
- exonware/xwsystem/shared/errors.py +2 -2
- exonware/xwsystem/shared/xwobject.py +316 -0
- exonware/xwsystem/structures/__init__.py +1 -0
- exonware/xwsystem/structures/base.py +3 -2
- exonware/xwsystem/structures/circular_detector.py +15 -14
- exonware/xwsystem/structures/contracts.py +53 -76
- exonware/xwsystem/structures/defs.py +2 -1
- exonware/xwsystem/structures/errors.py +2 -1
- exonware/xwsystem/structures/tree_walker.py +2 -1
- exonware/xwsystem/threading/__init__.py +21 -4
- exonware/xwsystem/threading/async_primitives.py +6 -5
- exonware/xwsystem/threading/base.py +3 -2
- exonware/xwsystem/threading/contracts.py +87 -143
- exonware/xwsystem/threading/defs.py +2 -1
- exonware/xwsystem/threading/errors.py +2 -1
- exonware/xwsystem/threading/facade.py +175 -0
- exonware/xwsystem/threading/locks.py +1 -0
- exonware/xwsystem/threading/safe_factory.py +1 -0
- exonware/xwsystem/utils/__init__.py +40 -0
- exonware/xwsystem/utils/base.py +22 -21
- exonware/xwsystem/utils/contracts.py +50 -73
- exonware/xwsystem/utils/dt/__init__.py +19 -3
- exonware/xwsystem/utils/dt/base.py +5 -4
- exonware/xwsystem/utils/dt/contracts.py +22 -29
- exonware/xwsystem/utils/dt/defs.py +2 -1
- exonware/xwsystem/utils/dt/errors.py +2 -5
- exonware/xwsystem/utils/dt/formatting.py +88 -2
- exonware/xwsystem/utils/dt/humanize.py +10 -9
- exonware/xwsystem/utils/dt/parsing.py +56 -5
- exonware/xwsystem/utils/dt/timezone_utils.py +2 -24
- exonware/xwsystem/utils/errors.py +2 -4
- exonware/xwsystem/utils/paths.py +1 -0
- exonware/xwsystem/utils/string.py +49 -0
- exonware/xwsystem/utils/test_runner.py +185 -0
- exonware/xwsystem/utils/utils_contracts.py +2 -1
- exonware/xwsystem/utils/web.py +110 -0
- exonware/xwsystem/validation/__init__.py +25 -1
- exonware/xwsystem/validation/base.py +6 -5
- exonware/xwsystem/validation/contracts.py +29 -41
- exonware/xwsystem/validation/data_validator.py +1 -0
- exonware/xwsystem/validation/declarative.py +11 -8
- exonware/xwsystem/validation/defs.py +2 -1
- exonware/xwsystem/validation/errors.py +2 -1
- exonware/xwsystem/validation/facade.py +198 -0
- exonware/xwsystem/validation/fluent_validator.py +22 -19
- exonware/xwsystem/validation/schema_discovery.py +210 -0
- exonware/xwsystem/validation/type_safety.py +2 -1
- exonware/xwsystem/version.py +2 -2
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/METADATA +71 -4
- exonware_xwsystem-0.1.0.3.dist-info/RECORD +337 -0
- exonware/xwsystem/cli/__init__.py +0 -43
- exonware/xwsystem/cli/console.py +0 -113
- exonware/xwsystem/cli/defs.py +0 -134
- exonware/xwsystem/conf.py +0 -44
- exonware/xwsystem/security/auth.py +0 -484
- exonware_xwsystem-0.1.0.1.dist-info/RECORD +0 -284
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/WHEEL +0 -0
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/auto_serializer.py
|
|
1
2
|
#exonware\xwsystem\serialization\auto_serializer.py
|
|
2
3
|
"""
|
|
3
4
|
Company: eXonware.com
|
|
4
5
|
Author: Eng. Muhammad AlShehri
|
|
5
6
|
Email: connect@exonware.com
|
|
6
|
-
Version: 0.1.0.
|
|
7
|
+
Version: 0.1.0.3
|
|
7
8
|
Generation Date: September 04, 2025
|
|
8
9
|
|
|
9
10
|
Automatic serializer that detects format and delegates to appropriate serializer.
|
|
10
11
|
"""
|
|
11
12
|
|
|
12
13
|
from pathlib import Path
|
|
13
|
-
from typing import Any, Optional
|
|
14
|
+
from typing import Any, Optional
|
|
14
15
|
|
|
15
16
|
from .format_detector import FormatDetector, detect_format
|
|
16
17
|
from .contracts import ISerialization
|
|
@@ -43,8 +44,11 @@ class AutoSerializer:
|
|
|
43
44
|
"""
|
|
44
45
|
Get serializer class for format name.
|
|
45
46
|
|
|
47
|
+
First checks UniversalCodecRegistry for auto-registered formats (like XWJSON),
|
|
48
|
+
then falls back to hardcoded module_map for xwsystem formats.
|
|
49
|
+
|
|
46
50
|
Args:
|
|
47
|
-
format_name: Format name (e.g., 'JSON', 'YAML')
|
|
51
|
+
format_name: Format name (e.g., 'JSON', 'YAML', 'XWJSON')
|
|
48
52
|
|
|
49
53
|
Returns:
|
|
50
54
|
Serializer class
|
|
@@ -52,10 +56,40 @@ class AutoSerializer:
|
|
|
52
56
|
Raises:
|
|
53
57
|
ImportError: If format not available
|
|
54
58
|
"""
|
|
59
|
+
# First, try UniversalCodecRegistry for auto-registered formats
|
|
60
|
+
# All auto-registering formats (xwjson, xwformats, xwsyntax, etc.) register themselves
|
|
61
|
+
# when their modules are imported, so we just check the registry - no special cases needed
|
|
62
|
+
try:
|
|
63
|
+
from ..codec.registry import get_registry
|
|
64
|
+
registry = get_registry()
|
|
65
|
+
# Try format name as-is, then lowercase, then uppercase, then aliases
|
|
66
|
+
codec = (registry.get_by_id(format_name) or
|
|
67
|
+
registry.get_by_id(format_name.lower()) or
|
|
68
|
+
registry.get_by_id(format_name.upper()) or
|
|
69
|
+
registry.get_by_alias(format_name) or
|
|
70
|
+
registry.get_by_alias(format_name.lower()) or
|
|
71
|
+
registry.get_by_alias(format_name.upper()))
|
|
72
|
+
if codec:
|
|
73
|
+
# Check if codec implements ISerialization (has dumps/loads)
|
|
74
|
+
# Codec adapters (ICodec only) have encode/decode but NOT dumps/loads
|
|
75
|
+
if hasattr(codec, 'dumps') and hasattr(codec, 'loads'):
|
|
76
|
+
# This is a full ISerialization implementation
|
|
77
|
+
logger.debug(f"Found ISerialization codec in registry for format: {format_name}")
|
|
78
|
+
return type(codec)
|
|
79
|
+
else:
|
|
80
|
+
# This is only ICodec (codec adapter), skip it
|
|
81
|
+
logger.debug(f"Skipping codec adapter (ICodec only) for format: {format_name}, needs ISerialization")
|
|
82
|
+
pass
|
|
83
|
+
except Exception as e:
|
|
84
|
+
# Registry not available or format not found, continue to hardcoded map
|
|
85
|
+
logger.debug(f"Registry lookup failed for {format_name}: {e}")
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
# Fallback to hardcoded module_map for xwsystem formats
|
|
55
89
|
# Dynamic import to avoid circular dependencies.
|
|
56
90
|
# Root cause fix: import concrete serializers from the canonical
|
|
57
91
|
# xwsystem.io.serialization.formats packages instead of a parallel
|
|
58
|
-
# exonware.xwsystem.serialization namespace (
|
|
92
|
+
# exonware.xwsystem.serialization namespace (not expected to exist).
|
|
59
93
|
module_map = {
|
|
60
94
|
# Text formats
|
|
61
95
|
'JSON': ('io.serialization.formats.text.json', 'JsonSerializer'),
|
|
@@ -111,6 +145,8 @@ class AutoSerializer:
|
|
|
111
145
|
"""
|
|
112
146
|
Get cached serializer instance for format.
|
|
113
147
|
|
|
148
|
+
First checks UniversalCodecRegistry for instances, then creates from class.
|
|
149
|
+
|
|
114
150
|
Args:
|
|
115
151
|
format_name: Format name
|
|
116
152
|
|
|
@@ -118,6 +154,36 @@ class AutoSerializer:
|
|
|
118
154
|
Serializer instance
|
|
119
155
|
"""
|
|
120
156
|
if format_name not in self._serializer_cache:
|
|
157
|
+
# First try to get instance from registry (for auto-registered formats)
|
|
158
|
+
try:
|
|
159
|
+
from ..codec.registry import get_registry
|
|
160
|
+
registry = get_registry()
|
|
161
|
+
# Try format name as-is, then lowercase, then uppercase, then aliases
|
|
162
|
+
codec = (registry.get_by_id(format_name) or
|
|
163
|
+
registry.get_by_id(format_name.lower()) or
|
|
164
|
+
registry.get_by_id(format_name.upper()) or
|
|
165
|
+
registry.get_by_alias(format_name) or
|
|
166
|
+
registry.get_by_alias(format_name.lower()) or
|
|
167
|
+
registry.get_by_alias(format_name.upper()))
|
|
168
|
+
# Check if codec has ISerialization interface (dumps/loads methods)
|
|
169
|
+
# Codec adapters (ICodec only) have encode/decode but NOT dumps/loads
|
|
170
|
+
# We need ISerialization (which extends ICodec) with dumps/loads
|
|
171
|
+
if codec and hasattr(codec, 'dumps') and hasattr(codec, 'loads'):
|
|
172
|
+
# This is a full ISerialization implementation
|
|
173
|
+
self._serializer_cache[format_name] = codec
|
|
174
|
+
logger.debug(f"Using registry serializer (ISerialization) for format: {format_name}")
|
|
175
|
+
return codec
|
|
176
|
+
elif codec and hasattr(codec, 'encode') and not hasattr(codec, 'dumps'):
|
|
177
|
+
# This is only ICodec (codec adapter), not ISerialization
|
|
178
|
+
# Skip it - we need a serializer with dumps/loads for detect_and_serialize
|
|
179
|
+
logger.debug(f"Skipping codec adapter (ICodec only) for format: {format_name}, needs ISerialization")
|
|
180
|
+
pass
|
|
181
|
+
except (ImportError, AttributeError, TypeError) as e:
|
|
182
|
+
# Registry not available or codec not compatible, create from class
|
|
183
|
+
logger.debug(f"Registry lookup failed for {format_name}: {e}")
|
|
184
|
+
pass
|
|
185
|
+
|
|
186
|
+
# Fallback: create instance from class
|
|
121
187
|
serializer_class = self._get_serializer_class(format_name)
|
|
122
188
|
self._serializer_cache[format_name] = serializer_class()
|
|
123
189
|
logger.debug(f"Created serializer for format: {format_name}")
|
|
@@ -127,10 +193,10 @@ class AutoSerializer:
|
|
|
127
193
|
def detect_and_serialize(
|
|
128
194
|
self,
|
|
129
195
|
data: Any,
|
|
130
|
-
file_path: Optional[
|
|
196
|
+
file_path: Optional[str | Path] = None,
|
|
131
197
|
format_hint: Optional[str] = None,
|
|
132
198
|
**opts
|
|
133
|
-
) ->
|
|
199
|
+
) -> str | bytes:
|
|
134
200
|
"""
|
|
135
201
|
Auto-detect format and serialize data.
|
|
136
202
|
|
|
@@ -139,6 +205,8 @@ class AutoSerializer:
|
|
|
139
205
|
file_path: Optional file path for format detection
|
|
140
206
|
format_hint: Optional format hint to use
|
|
141
207
|
**opts: Additional serializer-specific options (pretty, indent, etc.)
|
|
208
|
+
Universal options (sorted, pretty, compact, canonical) are automatically
|
|
209
|
+
mapped to format-specific options.
|
|
142
210
|
|
|
143
211
|
Returns:
|
|
144
212
|
Serialized data
|
|
@@ -155,13 +223,34 @@ class AutoSerializer:
|
|
|
155
223
|
format_name = self._default_format
|
|
156
224
|
logger.debug(f"Using default format: {format_name}")
|
|
157
225
|
|
|
226
|
+
# Map universal options to format-specific options
|
|
227
|
+
try:
|
|
228
|
+
from .universal_options import map_universal_options
|
|
229
|
+
# Check if any universal options are present
|
|
230
|
+
universal_option_names = {'pretty', 'compact', 'sorted', 'canonical', 'indent',
|
|
231
|
+
'ensure_ascii', 'allow_nan', 'strip_whitespace',
|
|
232
|
+
'preserve_quotes', 'declaration', 'encoding',
|
|
233
|
+
'line_separator', 'item_separator'}
|
|
234
|
+
has_universal_options = any(key in universal_option_names for key in opts.keys())
|
|
235
|
+
|
|
236
|
+
if has_universal_options:
|
|
237
|
+
# Map universal options to format-specific options
|
|
238
|
+
format_specific_opts = map_universal_options(format_name, **opts)
|
|
239
|
+
# Merge with any remaining non-universal options
|
|
240
|
+
remaining_opts = {k: v for k, v in opts.items() if k not in universal_option_names}
|
|
241
|
+
format_specific_opts.update(remaining_opts)
|
|
242
|
+
opts = format_specific_opts
|
|
243
|
+
except ImportError:
|
|
244
|
+
# Universal options module not available, use opts as-is
|
|
245
|
+
logger.debug("Universal options module not available, using options as-is")
|
|
246
|
+
|
|
158
247
|
serializer = self._get_serializer(format_name)
|
|
159
248
|
return serializer.dumps(data, **opts)
|
|
160
249
|
|
|
161
250
|
def detect_and_deserialize(
|
|
162
251
|
self,
|
|
163
|
-
data:
|
|
164
|
-
file_path: Optional[
|
|
252
|
+
data: str | bytes,
|
|
253
|
+
file_path: Optional[str | Path] = None,
|
|
165
254
|
format_hint: Optional[str] = None
|
|
166
255
|
) -> Any:
|
|
167
256
|
"""
|
|
@@ -197,7 +286,7 @@ class AutoSerializer:
|
|
|
197
286
|
def auto_save_file(
|
|
198
287
|
self,
|
|
199
288
|
data: Any,
|
|
200
|
-
file_path:
|
|
289
|
+
file_path: str | Path,
|
|
201
290
|
format_hint: Optional[str] = None
|
|
202
291
|
) -> None:
|
|
203
292
|
"""
|
|
@@ -222,9 +311,46 @@ class AutoSerializer:
|
|
|
222
311
|
serializer.save_file(data, file_path)
|
|
223
312
|
logger.info(f"Saved data to {file_path} using {format_name} format")
|
|
224
313
|
|
|
314
|
+
def save_file(
|
|
315
|
+
self,
|
|
316
|
+
data: Any,
|
|
317
|
+
file_path: str | Path,
|
|
318
|
+
format_hint: Optional[str] = None,
|
|
319
|
+
**opts
|
|
320
|
+
) -> None:
|
|
321
|
+
"""
|
|
322
|
+
Auto-detect format and save to file (alias for auto_save_file).
|
|
323
|
+
|
|
324
|
+
Args:
|
|
325
|
+
data: Data to save
|
|
326
|
+
file_path: File path (format detected from extension)
|
|
327
|
+
format_hint: Optional format hint to override detection
|
|
328
|
+
**opts: Additional options (passed to serializer)
|
|
329
|
+
"""
|
|
330
|
+
self.auto_save_file(data, file_path, format_hint)
|
|
331
|
+
|
|
332
|
+
def load_file(
|
|
333
|
+
self,
|
|
334
|
+
file_path: str | Path,
|
|
335
|
+
format_hint: Optional[str] = None,
|
|
336
|
+
**opts
|
|
337
|
+
) -> Any:
|
|
338
|
+
"""
|
|
339
|
+
Auto-detect format and load from file (alias for auto_load_file).
|
|
340
|
+
|
|
341
|
+
Args:
|
|
342
|
+
file_path: File path to load
|
|
343
|
+
format_hint: Optional format hint to override detection
|
|
344
|
+
**opts: Additional options (passed to serializer)
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
Loaded data
|
|
348
|
+
"""
|
|
349
|
+
return self.auto_load_file(file_path, format_hint)
|
|
350
|
+
|
|
225
351
|
def auto_load_file(
|
|
226
352
|
self,
|
|
227
|
-
file_path:
|
|
353
|
+
file_path: str | Path,
|
|
228
354
|
format_hint: Optional[str] = None
|
|
229
355
|
) -> Any:
|
|
230
356
|
"""
|
|
@@ -280,7 +406,7 @@ class AutoSerializer:
|
|
|
280
406
|
async def auto_save_file_async(
|
|
281
407
|
self,
|
|
282
408
|
data: Any,
|
|
283
|
-
file_path:
|
|
409
|
+
file_path: str | Path,
|
|
284
410
|
format_hint: Optional[str] = None
|
|
285
411
|
) -> None:
|
|
286
412
|
"""
|
|
@@ -306,7 +432,7 @@ class AutoSerializer:
|
|
|
306
432
|
|
|
307
433
|
async def auto_load_file_async(
|
|
308
434
|
self,
|
|
309
|
-
file_path:
|
|
435
|
+
file_path: str | Path,
|
|
310
436
|
format_hint: Optional[str] = None
|
|
311
437
|
) -> Any:
|
|
312
438
|
"""
|
|
@@ -342,8 +468,8 @@ class AutoSerializer:
|
|
|
342
468
|
|
|
343
469
|
def get_format_suggestions(
|
|
344
470
|
self,
|
|
345
|
-
data:
|
|
346
|
-
file_path: Optional[
|
|
471
|
+
data: str | bytes,
|
|
472
|
+
file_path: Optional[str | Path] = None
|
|
347
473
|
) -> list[tuple[str, float]]:
|
|
348
474
|
"""
|
|
349
475
|
Get format suggestions for given data.
|
|
@@ -372,10 +498,10 @@ _global_auto_serializer = AutoSerializer()
|
|
|
372
498
|
|
|
373
499
|
def auto_serialize(
|
|
374
500
|
data: Any,
|
|
375
|
-
file_path: Optional[
|
|
501
|
+
file_path: Optional[str | Path] = None,
|
|
376
502
|
format_hint: Optional[str] = None,
|
|
377
503
|
**opts
|
|
378
|
-
) ->
|
|
504
|
+
) -> str | bytes:
|
|
379
505
|
"""
|
|
380
506
|
Convenience function for auto-serialization.
|
|
381
507
|
|
|
@@ -391,8 +517,8 @@ def auto_serialize(
|
|
|
391
517
|
return _global_auto_serializer.detect_and_serialize(data, file_path, format_hint, **opts)
|
|
392
518
|
|
|
393
519
|
def auto_deserialize(
|
|
394
|
-
data:
|
|
395
|
-
file_path: Optional[
|
|
520
|
+
data: str | bytes,
|
|
521
|
+
file_path: Optional[str | Path] = None,
|
|
396
522
|
format_hint: Optional[str] = None
|
|
397
523
|
) -> Any:
|
|
398
524
|
"""
|
|
@@ -410,7 +536,7 @@ def auto_deserialize(
|
|
|
410
536
|
|
|
411
537
|
def auto_save_file(
|
|
412
538
|
data: Any,
|
|
413
|
-
file_path:
|
|
539
|
+
file_path: str | Path,
|
|
414
540
|
format_hint: Optional[str] = None
|
|
415
541
|
) -> None:
|
|
416
542
|
"""
|
|
@@ -424,7 +550,7 @@ def auto_save_file(
|
|
|
424
550
|
return _global_auto_serializer.auto_save_file(data, file_path, format_hint)
|
|
425
551
|
|
|
426
552
|
def auto_load_file(
|
|
427
|
-
file_path:
|
|
553
|
+
file_path: str | Path,
|
|
428
554
|
format_hint: Optional[str] = None
|
|
429
555
|
) -> Any:
|
|
430
556
|
"""
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/serialization/base.py
|
|
1
2
|
"""
|
|
2
3
|
Company: eXonware.com
|
|
3
4
|
Author: Eng. Muhammad AlShehri
|
|
4
5
|
Email: connect@exonware.com
|
|
5
|
-
Version: 0.1.0.
|
|
6
|
+
Version: 0.1.0.3
|
|
6
7
|
Generation Date: November 2, 2025
|
|
7
8
|
|
|
8
9
|
Serialization base classes - ASerialization abstract base.
|
|
@@ -13,9 +14,11 @@ Following I→A→XW pattern:
|
|
|
13
14
|
- XW: XW{Format}Serializer (concrete implementations)
|
|
14
15
|
"""
|
|
15
16
|
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
16
19
|
import asyncio
|
|
17
20
|
from abc import ABC, abstractmethod, ABCMeta
|
|
18
|
-
from typing import Any,
|
|
21
|
+
from typing import Any, Optional, BinaryIO, TextIO, AsyncIterator, Iterator, TYPE_CHECKING
|
|
19
22
|
# Root cause: Migrating to Python 3.12 built-in generic syntax for consistency
|
|
20
23
|
# Priority #3: Maintainability - Modern type annotations improve code clarity
|
|
21
24
|
from pathlib import Path
|
|
@@ -31,7 +34,41 @@ if TYPE_CHECKING:
|
|
|
31
34
|
from .schema_registry import SchemaInfo
|
|
32
35
|
|
|
33
36
|
|
|
34
|
-
|
|
37
|
+
# ============================================================================
|
|
38
|
+
# MODULE-LEVEL CACHE FOR SERIALIZER INSTANCES BY FILE PATH
|
|
39
|
+
# ============================================================================
|
|
40
|
+
|
|
41
|
+
# Module-level cache: file_path -> serializer_instance
|
|
42
|
+
# All serializers (JsonSerializer, YamlSerializer, XWSerializer, etc.) use this
|
|
43
|
+
# Benefits: O(1) lookup, automatic LRU eviction, thread-safe, statistics
|
|
44
|
+
from ...caching import create_cache
|
|
45
|
+
# Use flexible create_cache() to allow configuration via environment/settings
|
|
46
|
+
# Defaults to FunctoolsLRUCache
|
|
47
|
+
_file_serializer_cache = create_cache(capacity=100, namespace='xwsystem.serialization', name="FileSerializerCache")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def get_cached_serializer_for_path(file_path: str | Path) -> Optional[ISerialization]:
|
|
51
|
+
"""
|
|
52
|
+
Get cached serializer instance for file path, if available.
|
|
53
|
+
|
|
54
|
+
This function allows retrieving a previously cached serializer instance
|
|
55
|
+
for a given file path. Serializers are automatically cached when
|
|
56
|
+
save_file() or load_file() is called.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
file_path: Path to the file
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
Cached serializer instance if available, None otherwise
|
|
63
|
+
"""
|
|
64
|
+
if _file_serializer_cache is None:
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
path_str = str(Path(file_path).resolve())
|
|
68
|
+
return _file_serializer_cache.get(path_str)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class ASerialization(ACodec[Any, bytes | str], ISerialization):
|
|
35
72
|
"""
|
|
36
73
|
Abstract base class for serialization - follows I→A→XW pattern.
|
|
37
74
|
|
|
@@ -69,11 +106,11 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
69
106
|
super().__init__(max_depth=max_depth, max_size_mb=max_size_mb)
|
|
70
107
|
|
|
71
108
|
# ========================================================================
|
|
72
|
-
# CORE CODEC METHODS (
|
|
109
|
+
# CORE CODEC METHODS (Implement in subclasses)
|
|
73
110
|
# ========================================================================
|
|
74
111
|
|
|
75
112
|
@abstractmethod
|
|
76
|
-
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) ->
|
|
113
|
+
def encode(self, value: Any, *, options: Optional[EncodeOptions] = None) -> bytes | str:
|
|
77
114
|
"""
|
|
78
115
|
Encode data to representation - must implement in subclass.
|
|
79
116
|
|
|
@@ -83,7 +120,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
83
120
|
pass
|
|
84
121
|
|
|
85
122
|
@abstractmethod
|
|
86
|
-
def decode(self, repr:
|
|
123
|
+
def decode(self, repr: bytes | str, *, options: Optional[DecodeOptions] = None) -> Any:
|
|
87
124
|
"""
|
|
88
125
|
Decode representation to data - must implement in subclass.
|
|
89
126
|
|
|
@@ -93,7 +130,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
93
130
|
pass
|
|
94
131
|
|
|
95
132
|
# ========================================================================
|
|
96
|
-
# METADATA PROPERTIES (
|
|
133
|
+
# METADATA PROPERTIES (Implement in subclasses)
|
|
97
134
|
# ========================================================================
|
|
98
135
|
|
|
99
136
|
@property
|
|
@@ -301,7 +338,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
301
338
|
# FILE I/O METHODS (Default implementations using encode/decode)
|
|
302
339
|
# ========================================================================
|
|
303
340
|
|
|
304
|
-
def save_file(self, data: Any, file_path:
|
|
341
|
+
def save_file(self, data: Any, file_path: str | Path, **options) -> None:
|
|
305
342
|
"""
|
|
306
343
|
Save data to file with atomic operations.
|
|
307
344
|
|
|
@@ -310,6 +347,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
310
347
|
2. Encode data using encode()
|
|
311
348
|
3. Write to file using Path.write_bytes() or write_text()
|
|
312
349
|
4. Uses atomic operations if configured
|
|
350
|
+
5. Caches serializer instance by file path for performance
|
|
313
351
|
|
|
314
352
|
Args:
|
|
315
353
|
data: Data to serialize and save
|
|
@@ -321,6 +359,12 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
321
359
|
"""
|
|
322
360
|
try:
|
|
323
361
|
path = Path(file_path)
|
|
362
|
+
path_str = str(path.resolve())
|
|
363
|
+
|
|
364
|
+
# Cache this serializer instance for this file path
|
|
365
|
+
# This enables reuse if the same file is accessed again
|
|
366
|
+
if _file_serializer_cache is not None:
|
|
367
|
+
_file_serializer_cache.put(path_str, self)
|
|
324
368
|
|
|
325
369
|
# Ensure parent directory exists
|
|
326
370
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -349,13 +393,14 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
349
393
|
original_error=e
|
|
350
394
|
)
|
|
351
395
|
|
|
352
|
-
def load_file(self, file_path:
|
|
396
|
+
def load_file(self, file_path: str | Path, **options) -> Any:
|
|
353
397
|
"""
|
|
354
398
|
Load data from file.
|
|
355
399
|
|
|
356
400
|
Default implementation:
|
|
357
401
|
1. Read from file using Path.read_bytes() or read_text()
|
|
358
402
|
2. Decode data using decode()
|
|
403
|
+
3. Caches serializer instance by file path for performance
|
|
359
404
|
|
|
360
405
|
Args:
|
|
361
406
|
file_path: Path to load from
|
|
@@ -369,6 +414,12 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
369
414
|
"""
|
|
370
415
|
try:
|
|
371
416
|
path = Path(file_path)
|
|
417
|
+
path_str = str(path.resolve())
|
|
418
|
+
|
|
419
|
+
# Cache this serializer instance for this file path
|
|
420
|
+
# This enables reuse if the same file is accessed again
|
|
421
|
+
if _file_serializer_cache is not None:
|
|
422
|
+
_file_serializer_cache.put(path_str, self)
|
|
372
423
|
|
|
373
424
|
if not path.exists():
|
|
374
425
|
raise FileNotFoundError(f"File not found: {path}")
|
|
@@ -401,7 +452,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
401
452
|
|
|
402
453
|
def stream_read_record(
|
|
403
454
|
self,
|
|
404
|
-
file_path:
|
|
455
|
+
file_path: str | Path,
|
|
405
456
|
match: callable,
|
|
406
457
|
projection: Optional[list[Any]] = None,
|
|
407
458
|
**options: Any,
|
|
@@ -430,7 +481,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
430
481
|
|
|
431
482
|
def stream_update_record(
|
|
432
483
|
self,
|
|
433
|
-
file_path:
|
|
484
|
+
file_path: str | Path,
|
|
434
485
|
match: callable,
|
|
435
486
|
updater: callable,
|
|
436
487
|
*,
|
|
@@ -469,7 +520,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
469
520
|
|
|
470
521
|
def get_record_page(
|
|
471
522
|
self,
|
|
472
|
-
file_path:
|
|
523
|
+
file_path: str | Path,
|
|
473
524
|
page_number: int,
|
|
474
525
|
page_size: int,
|
|
475
526
|
**options: Any,
|
|
@@ -500,7 +551,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
500
551
|
|
|
501
552
|
def get_record_by_id(
|
|
502
553
|
self,
|
|
503
|
-
file_path:
|
|
554
|
+
file_path: str | Path,
|
|
504
555
|
id_value: Any,
|
|
505
556
|
*,
|
|
506
557
|
id_field: str = "id",
|
|
@@ -572,7 +623,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
572
623
|
# STREAMING METHODS (Default implementations)
|
|
573
624
|
# ========================================================================
|
|
574
625
|
|
|
575
|
-
def iter_serialize(self, data: Any, chunk_size: int = 8192) -> Iterator[
|
|
626
|
+
def iter_serialize(self, data: Any, chunk_size: int = 8192) -> Iterator[str | bytes]:
|
|
576
627
|
"""
|
|
577
628
|
Stream serialize data in chunks.
|
|
578
629
|
|
|
@@ -596,7 +647,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
596
647
|
for i in range(0, len(repr_data), chunk_size):
|
|
597
648
|
yield repr_data[i:i + chunk_size]
|
|
598
649
|
|
|
599
|
-
def iter_deserialize(self, src:
|
|
650
|
+
def iter_deserialize(self, src: TextIO | BinaryIO | Iterator[str | bytes]) -> Any:
|
|
600
651
|
"""
|
|
601
652
|
Stream deserialize data from chunks.
|
|
602
653
|
|
|
@@ -625,7 +676,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
625
676
|
# ASYNC METHODS (Default implementations using asyncio.to_thread)
|
|
626
677
|
# ========================================================================
|
|
627
678
|
|
|
628
|
-
async def save_file_async(self, data: Any, file_path:
|
|
679
|
+
async def save_file_async(self, data: Any, file_path: str | Path, **options) -> None:
|
|
629
680
|
"""
|
|
630
681
|
Async save data to file.
|
|
631
682
|
|
|
@@ -639,7 +690,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
639
690
|
"""
|
|
640
691
|
await asyncio.to_thread(self.save_file, data, file_path, **options)
|
|
641
692
|
|
|
642
|
-
async def load_file_async(self, file_path:
|
|
693
|
+
async def load_file_async(self, file_path: str | Path, **options) -> Any:
|
|
643
694
|
"""
|
|
644
695
|
Async load data from file.
|
|
645
696
|
|
|
@@ -655,7 +706,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
655
706
|
"""
|
|
656
707
|
return await asyncio.to_thread(self.load_file, file_path, **options)
|
|
657
708
|
|
|
658
|
-
async def stream_serialize(self, data: Any, chunk_size: int = 8192) -> AsyncIterator[
|
|
709
|
+
async def stream_serialize(self, data: Any, chunk_size: int = 8192) -> AsyncIterator[str | bytes]:
|
|
659
710
|
"""
|
|
660
711
|
Async stream serialize data in chunks.
|
|
661
712
|
|
|
@@ -673,7 +724,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
673
724
|
yield chunk
|
|
674
725
|
await asyncio.sleep(0) # Yield control
|
|
675
726
|
|
|
676
|
-
async def stream_deserialize(self, data_stream: AsyncIterator[
|
|
727
|
+
async def stream_deserialize(self, data_stream: AsyncIterator[str | bytes]) -> Any:
|
|
677
728
|
"""
|
|
678
729
|
Async stream deserialize data from chunks.
|
|
679
730
|
|
|
@@ -703,7 +754,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
703
754
|
|
|
704
755
|
def atomic_update_path(
|
|
705
756
|
self,
|
|
706
|
-
file_path:
|
|
757
|
+
file_path: str | Path,
|
|
707
758
|
path: str,
|
|
708
759
|
value: Any,
|
|
709
760
|
**options
|
|
@@ -743,13 +794,13 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
743
794
|
|
|
744
795
|
# Load entire file
|
|
745
796
|
# For large files, skip size validation (they use lazy loading/streaming)
|
|
746
|
-
# Root cause: Large files (10GB+)
|
|
797
|
+
# Root cause: Large files (10GB+) use atomic path operations without full validation
|
|
747
798
|
# Solution: Skip size check for atomic operations (depth check still performed)
|
|
748
799
|
large_file_options = {**options, 'skip_size_check': True}
|
|
749
800
|
data = self.load_file(file_path, **large_file_options)
|
|
750
801
|
|
|
751
802
|
# Update path in memory (simple dict/list update for now)
|
|
752
|
-
# Subclasses
|
|
803
|
+
# Subclasses override with format-specific logic
|
|
753
804
|
if isinstance(data, dict) and path.startswith('/'):
|
|
754
805
|
# Simple JSONPointer-like path handling
|
|
755
806
|
path_parts = path.strip('/').split('/')
|
|
@@ -795,7 +846,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
795
846
|
|
|
796
847
|
def atomic_read_path(
|
|
797
848
|
self,
|
|
798
|
-
file_path:
|
|
849
|
+
file_path: str | Path,
|
|
799
850
|
path: str,
|
|
800
851
|
**options
|
|
801
852
|
) -> Any:
|
|
@@ -832,7 +883,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
832
883
|
|
|
833
884
|
# Load entire file
|
|
834
885
|
# For large files, skip size validation (they use lazy loading/streaming)
|
|
835
|
-
# Root cause: Large files (10GB+)
|
|
886
|
+
# Root cause: Large files (10GB+) use atomic path operations without full validation
|
|
836
887
|
# Solution: Skip size check for atomic operations (depth check still performed)
|
|
837
888
|
large_file_options = {**options, 'skip_size_check': True}
|
|
838
889
|
data = self.load_file(file_path, **large_file_options)
|
|
@@ -898,7 +949,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
898
949
|
def incremental_save(
|
|
899
950
|
self,
|
|
900
951
|
items: Iterator[Any],
|
|
901
|
-
file_path:
|
|
952
|
+
file_path: str | Path,
|
|
902
953
|
**options
|
|
903
954
|
) -> None:
|
|
904
955
|
"""
|
|
@@ -939,7 +990,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
939
990
|
|
|
940
991
|
def incremental_load(
|
|
941
992
|
self,
|
|
942
|
-
file_path:
|
|
993
|
+
file_path: str | Path,
|
|
943
994
|
**options
|
|
944
995
|
) -> Iterator[Any]:
|
|
945
996
|
"""
|
|
@@ -986,7 +1037,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
986
1037
|
|
|
987
1038
|
def query(
|
|
988
1039
|
self,
|
|
989
|
-
file_path:
|
|
1040
|
+
file_path: str | Path,
|
|
990
1041
|
query_expr: str,
|
|
991
1042
|
**options
|
|
992
1043
|
) -> Any:
|
|
@@ -1021,7 +1072,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
1021
1072
|
)
|
|
1022
1073
|
|
|
1023
1074
|
# Root cause fixed: Base class doesn't implement queries - raise immediately
|
|
1024
|
-
# Subclasses
|
|
1075
|
+
# Subclasses override this method to provide actual query implementation
|
|
1025
1076
|
# Priority #4: Performance - Don't load file if operation will fail
|
|
1026
1077
|
raise NotImplementedError(
|
|
1027
1078
|
f"Query operations require format-specific implementation for {self.format_name}. "
|
|
@@ -1031,7 +1082,7 @@ class ASerialization(ACodec[Any, Union[bytes, str]], ISerialization):
|
|
|
1031
1082
|
|
|
1032
1083
|
def merge(
|
|
1033
1084
|
self,
|
|
1034
|
-
file_path:
|
|
1085
|
+
file_path: str | Path,
|
|
1035
1086
|
updates: dict[str, Any],
|
|
1036
1087
|
**options
|
|
1037
1088
|
) -> None:
|
|
@@ -1115,17 +1166,17 @@ class ASchemaRegistry(ABC):
|
|
|
1115
1166
|
"""Abstract base class for schema registry implementations."""
|
|
1116
1167
|
|
|
1117
1168
|
@abstractmethod
|
|
1118
|
-
async def register_schema(self, subject: str, schema: str, schema_type: str = "AVRO") ->
|
|
1169
|
+
async def register_schema(self, subject: str, schema: str, schema_type: str = "AVRO") -> SchemaInfo:
|
|
1119
1170
|
"""Register a new schema version."""
|
|
1120
1171
|
pass
|
|
1121
1172
|
|
|
1122
1173
|
@abstractmethod
|
|
1123
|
-
async def get_schema(self, schema_id: int) ->
|
|
1174
|
+
async def get_schema(self, schema_id: int) -> SchemaInfo:
|
|
1124
1175
|
"""Get schema by ID."""
|
|
1125
1176
|
pass
|
|
1126
1177
|
|
|
1127
1178
|
@abstractmethod
|
|
1128
|
-
async def get_latest_schema(self, subject: str) ->
|
|
1179
|
+
async def get_latest_schema(self, subject: str) -> SchemaInfo:
|
|
1129
1180
|
"""Get latest schema version for subject."""
|
|
1130
1181
|
pass
|
|
1131
1182
|
|
|
@@ -1140,7 +1191,6 @@ class ASchemaRegistry(ABC):
|
|
|
1140
1191
|
pass
|
|
1141
1192
|
|
|
1142
1193
|
@abstractmethod
|
|
1143
|
-
async def set_compatibility(self, subject: str, level:
|
|
1194
|
+
async def set_compatibility(self, subject: str, level: CompatibilityLevel) -> None:
|
|
1144
1195
|
"""Set compatibility level for subject."""
|
|
1145
1196
|
pass
|
|
1146
|
-
|