exonware-xwsystem 0.1.0.1__py3-none-any.whl → 0.1.0.4__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.4.dist-info}/METADATA +71 -4
- exonware_xwsystem-0.1.0.4.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.4.dist-info}/WHEEL +0 -0
- {exonware_xwsystem-0.1.0.1.dist-info → exonware_xwsystem-0.1.0.4.dist-info}/licenses/LICENSE +0 -0
exonware/xwsystem/io/base.py
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
|
+
#exonware/xwsystem/src/exonware/xwsystem/io/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.4
|
|
6
7
|
Generation Date: September 04, 2025
|
|
7
8
|
|
|
8
9
|
IO module base classes - abstract classes for input/output functionality.
|
|
9
10
|
"""
|
|
10
11
|
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
11
14
|
import os
|
|
12
15
|
import time
|
|
13
16
|
from abc import ABC, abstractmethod
|
|
14
|
-
from typing import Any, Optional,
|
|
17
|
+
from typing import Any, Optional, BinaryIO, TextIO
|
|
15
18
|
from pathlib import Path
|
|
16
19
|
from .contracts import FileMode, FileType, PathType, OperationResult, LockType, IFile, IFolder, IPath, IStream, IAsyncIO, IAtomicOperations, IBackupOperations, ITemporaryOperations, IUnifiedIO, IFileManager
|
|
17
20
|
|
|
@@ -23,10 +26,10 @@ from .contracts import FileMode, FileType, PathType, OperationResult, LockType,
|
|
|
23
26
|
class AFile(IFile, ABC):
|
|
24
27
|
"""Abstract base class for file operations with both static and instance methods."""
|
|
25
28
|
|
|
26
|
-
def __init__(self, file_path:
|
|
29
|
+
def __init__(self, file_path: str | Path):
|
|
27
30
|
"""Initialize file base."""
|
|
28
31
|
self.file_path = Path(file_path)
|
|
29
|
-
self._handle: Optional[
|
|
32
|
+
self._handle: Optional[TextIO | BinaryIO] = None
|
|
30
33
|
|
|
31
34
|
# ============================================================================
|
|
32
35
|
# INSTANCE METHODS
|
|
@@ -38,12 +41,12 @@ class AFile(IFile, ABC):
|
|
|
38
41
|
pass
|
|
39
42
|
|
|
40
43
|
@abstractmethod
|
|
41
|
-
def read(self, size: Optional[int] = None) ->
|
|
44
|
+
def read(self, size: Optional[int] = None) -> str | bytes:
|
|
42
45
|
"""Read from file."""
|
|
43
46
|
pass
|
|
44
47
|
|
|
45
48
|
@abstractmethod
|
|
46
|
-
def write(self, data:
|
|
49
|
+
def write(self, data: str | bytes) -> int:
|
|
47
50
|
"""Write to file."""
|
|
48
51
|
pass
|
|
49
52
|
|
|
@@ -63,17 +66,17 @@ class AFile(IFile, ABC):
|
|
|
63
66
|
pass
|
|
64
67
|
|
|
65
68
|
@abstractmethod
|
|
66
|
-
def save_as(self, path:
|
|
69
|
+
def save_as(self, path: str | Path, data: Any, **kwargs) -> bool:
|
|
67
70
|
"""Save data to specific path."""
|
|
68
71
|
pass
|
|
69
72
|
|
|
70
73
|
@abstractmethod
|
|
71
|
-
def to_file(self, path:
|
|
74
|
+
def to_file(self, path: str | Path, **kwargs) -> bool:
|
|
72
75
|
"""Write current object to file."""
|
|
73
76
|
pass
|
|
74
77
|
|
|
75
78
|
@abstractmethod
|
|
76
|
-
def from_file(self, path:
|
|
79
|
+
def from_file(self, path: str | Path, **kwargs) -> AFile:
|
|
77
80
|
"""Load object from file."""
|
|
78
81
|
pass
|
|
79
82
|
|
|
@@ -82,19 +85,19 @@ class AFile(IFile, ABC):
|
|
|
82
85
|
# ============================================================================
|
|
83
86
|
|
|
84
87
|
@staticmethod
|
|
85
|
-
def exists(path:
|
|
88
|
+
def exists(path: str | Path) -> bool:
|
|
86
89
|
"""Check if file exists."""
|
|
87
90
|
return Path(path).exists() and Path(path).is_file()
|
|
88
91
|
|
|
89
92
|
@staticmethod
|
|
90
|
-
def size(path:
|
|
93
|
+
def size(path: str | Path) -> int:
|
|
91
94
|
"""Get file size."""
|
|
92
95
|
if AFile.exists(path):
|
|
93
96
|
return Path(path).stat().st_size
|
|
94
97
|
return 0
|
|
95
98
|
|
|
96
99
|
@staticmethod
|
|
97
|
-
def delete(path:
|
|
100
|
+
def delete(path: str | Path) -> bool:
|
|
98
101
|
"""Delete file."""
|
|
99
102
|
try:
|
|
100
103
|
if AFile.exists(path):
|
|
@@ -105,7 +108,7 @@ class AFile(IFile, ABC):
|
|
|
105
108
|
return False
|
|
106
109
|
|
|
107
110
|
@staticmethod
|
|
108
|
-
def copy(source:
|
|
111
|
+
def copy(source: str | Path, destination: str | Path) -> bool:
|
|
109
112
|
"""Copy file."""
|
|
110
113
|
try:
|
|
111
114
|
import shutil
|
|
@@ -115,7 +118,7 @@ class AFile(IFile, ABC):
|
|
|
115
118
|
return False
|
|
116
119
|
|
|
117
120
|
@staticmethod
|
|
118
|
-
def move(source:
|
|
121
|
+
def move(source: str | Path, destination: str | Path) -> bool:
|
|
119
122
|
"""Move file."""
|
|
120
123
|
try:
|
|
121
124
|
import shutil
|
|
@@ -125,60 +128,60 @@ class AFile(IFile, ABC):
|
|
|
125
128
|
return False
|
|
126
129
|
|
|
127
130
|
@staticmethod
|
|
128
|
-
def rename(old_path:
|
|
131
|
+
def rename(old_path: str | Path, new_path: str | Path) -> bool:
|
|
129
132
|
"""Rename file."""
|
|
130
133
|
return AFile.move(old_path, new_path)
|
|
131
134
|
|
|
132
135
|
@staticmethod
|
|
133
|
-
def get_modified_time(path:
|
|
136
|
+
def get_modified_time(path: str | Path) -> float:
|
|
134
137
|
"""Get file modification time."""
|
|
135
138
|
if AFile.exists(path):
|
|
136
139
|
return Path(path).stat().st_mtime
|
|
137
140
|
return 0.0
|
|
138
141
|
|
|
139
142
|
@staticmethod
|
|
140
|
-
def get_created_time(path:
|
|
143
|
+
def get_created_time(path: str | Path) -> float:
|
|
141
144
|
"""Get file creation time."""
|
|
142
145
|
if AFile.exists(path):
|
|
143
146
|
return Path(path).stat().st_ctime
|
|
144
147
|
return 0.0
|
|
145
148
|
|
|
146
149
|
@staticmethod
|
|
147
|
-
def get_permissions(path:
|
|
150
|
+
def get_permissions(path: str | Path) -> int:
|
|
148
151
|
"""Get file permissions."""
|
|
149
152
|
if AFile.exists(path):
|
|
150
153
|
return Path(path).stat().st_mode
|
|
151
154
|
return 0
|
|
152
155
|
|
|
153
156
|
@staticmethod
|
|
154
|
-
def is_readable(path:
|
|
157
|
+
def is_readable(path: str | Path) -> bool:
|
|
155
158
|
"""Check if file is readable."""
|
|
156
159
|
return AFile.exists(path) and os.access(path, os.R_OK)
|
|
157
160
|
|
|
158
161
|
@staticmethod
|
|
159
|
-
def is_writable(path:
|
|
162
|
+
def is_writable(path: str | Path) -> bool:
|
|
160
163
|
"""Check if file is writable."""
|
|
161
164
|
return AFile.exists(path) and os.access(path, os.W_OK)
|
|
162
165
|
|
|
163
166
|
@staticmethod
|
|
164
|
-
def is_executable(path:
|
|
167
|
+
def is_executable(path: str | Path) -> bool:
|
|
165
168
|
"""Check if file is executable."""
|
|
166
169
|
return AFile.exists(path) and os.access(path, os.X_OK)
|
|
167
170
|
|
|
168
171
|
@staticmethod
|
|
169
|
-
def read_text(path:
|
|
172
|
+
def read_text(path: str | Path, encoding: str = 'utf-8') -> str:
|
|
170
173
|
"""Read file as text."""
|
|
171
174
|
with open(path, 'r', encoding=encoding) as f:
|
|
172
175
|
return f.read()
|
|
173
176
|
|
|
174
177
|
@staticmethod
|
|
175
|
-
def read_bytes(path:
|
|
178
|
+
def read_bytes(path: str | Path) -> bytes:
|
|
176
179
|
"""Read file as bytes."""
|
|
177
180
|
with open(path, 'rb') as f:
|
|
178
181
|
return f.read()
|
|
179
182
|
|
|
180
183
|
@staticmethod
|
|
181
|
-
def write_text(path:
|
|
184
|
+
def write_text(path: str | Path, content: str, encoding: str = 'utf-8') -> bool:
|
|
182
185
|
"""Write text to file."""
|
|
183
186
|
try:
|
|
184
187
|
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -189,7 +192,7 @@ class AFile(IFile, ABC):
|
|
|
189
192
|
return False
|
|
190
193
|
|
|
191
194
|
@staticmethod
|
|
192
|
-
def write_bytes(path:
|
|
195
|
+
def write_bytes(path: str | Path, content: bytes) -> bool:
|
|
193
196
|
"""Write bytes to file."""
|
|
194
197
|
try:
|
|
195
198
|
Path(path).parent.mkdir(parents=True, exist_ok=True)
|
|
@@ -200,7 +203,7 @@ class AFile(IFile, ABC):
|
|
|
200
203
|
return False
|
|
201
204
|
|
|
202
205
|
@staticmethod
|
|
203
|
-
def safe_read_text(path:
|
|
206
|
+
def safe_read_text(path: str | Path, encoding: str = 'utf-8') -> Optional[str]:
|
|
204
207
|
"""Safely read text file, returning None on error."""
|
|
205
208
|
try:
|
|
206
209
|
return AFile.read_text(path, encoding)
|
|
@@ -208,7 +211,7 @@ class AFile(IFile, ABC):
|
|
|
208
211
|
return None
|
|
209
212
|
|
|
210
213
|
@staticmethod
|
|
211
|
-
def safe_read_bytes(path:
|
|
214
|
+
def safe_read_bytes(path: str | Path) -> Optional[bytes]:
|
|
212
215
|
"""Safely read binary file, returning None on error."""
|
|
213
216
|
try:
|
|
214
217
|
return AFile.read_bytes(path)
|
|
@@ -216,12 +219,12 @@ class AFile(IFile, ABC):
|
|
|
216
219
|
return None
|
|
217
220
|
|
|
218
221
|
@staticmethod
|
|
219
|
-
def safe_write_text(path:
|
|
222
|
+
def safe_write_text(path: str | Path, content: str, encoding: str = 'utf-8') -> bool:
|
|
220
223
|
"""Safely write text to file."""
|
|
221
224
|
return AFile.write_text(path, content, encoding)
|
|
222
225
|
|
|
223
226
|
@staticmethod
|
|
224
|
-
def safe_write_bytes(path:
|
|
227
|
+
def safe_write_bytes(path: str | Path, content: bytes) -> bool:
|
|
225
228
|
"""Safely write bytes to file."""
|
|
226
229
|
return AFile.write_bytes(path, content)
|
|
227
230
|
|
|
@@ -230,22 +233,23 @@ class AFile(IFile, ABC):
|
|
|
230
233
|
# ============================================================================
|
|
231
234
|
|
|
232
235
|
@staticmethod
|
|
233
|
-
def atomic_write(file_path:
|
|
236
|
+
def atomic_write(file_path: str | Path, data: str | bytes,
|
|
234
237
|
backup: bool = True) -> OperationResult:
|
|
235
238
|
"""Atomically write data to file (static version)."""
|
|
236
239
|
from .common.atomic import AtomicFileWriter
|
|
237
240
|
try:
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
writer.write(data
|
|
241
|
-
|
|
241
|
+
if isinstance(data, bytes):
|
|
242
|
+
with AtomicFileWriter(Path(file_path), mode="wb", backup=backup) as writer:
|
|
243
|
+
writer.write(data)
|
|
244
|
+
else:
|
|
245
|
+
with AtomicFileWriter(Path(file_path), mode="w", encoding="utf-8", backup=backup) as writer:
|
|
242
246
|
writer.write(data)
|
|
243
247
|
return OperationResult.SUCCESS
|
|
244
248
|
except Exception:
|
|
245
249
|
return OperationResult.FAILED
|
|
246
250
|
|
|
247
251
|
@staticmethod
|
|
248
|
-
def atomic_copy(source:
|
|
252
|
+
def atomic_copy(source: str | Path, destination: str | Path) -> OperationResult:
|
|
249
253
|
"""Atomically copy file (static version)."""
|
|
250
254
|
from .common.atomic import AtomicFileWriter
|
|
251
255
|
import shutil
|
|
@@ -258,7 +262,7 @@ class AFile(IFile, ABC):
|
|
|
258
262
|
return OperationResult.FAILED
|
|
259
263
|
|
|
260
264
|
@staticmethod
|
|
261
|
-
def atomic_move(source:
|
|
265
|
+
def atomic_move(source: str | Path, destination: str | Path) -> OperationResult:
|
|
262
266
|
"""Atomically move file (static version)."""
|
|
263
267
|
result = AFile.atomic_copy(source, destination)
|
|
264
268
|
if result == OperationResult.SUCCESS:
|
|
@@ -270,7 +274,7 @@ class AFile(IFile, ABC):
|
|
|
270
274
|
return OperationResult.FAILED
|
|
271
275
|
|
|
272
276
|
@staticmethod
|
|
273
|
-
def atomic_delete(file_path:
|
|
277
|
+
def atomic_delete(file_path: str | Path, backup: bool = True) -> OperationResult:
|
|
274
278
|
"""Atomically delete file (static version)."""
|
|
275
279
|
target = Path(file_path)
|
|
276
280
|
try:
|
|
@@ -286,7 +290,7 @@ class AFile(IFile, ABC):
|
|
|
286
290
|
return OperationResult.FAILED
|
|
287
291
|
|
|
288
292
|
@staticmethod
|
|
289
|
-
def create_backup(source:
|
|
293
|
+
def create_backup(source: str | Path, backup_dir: str | Path) -> Optional[Path]:
|
|
290
294
|
"""Create backup of file (static version)."""
|
|
291
295
|
import shutil
|
|
292
296
|
source_path = Path(source)
|
|
@@ -303,7 +307,7 @@ class AFile(IFile, ABC):
|
|
|
303
307
|
return None
|
|
304
308
|
|
|
305
309
|
@staticmethod
|
|
306
|
-
def restore_backup(backup_path:
|
|
310
|
+
def restore_backup(backup_path: str | Path, target: str | Path) -> OperationResult:
|
|
307
311
|
"""Restore from backup (static version)."""
|
|
308
312
|
import shutil
|
|
309
313
|
try:
|
|
@@ -336,7 +340,7 @@ class AFile(IFile, ABC):
|
|
|
336
340
|
class AFolder(IFolder, ABC):
|
|
337
341
|
"""Abstract base class for folder operations with both static and instance methods."""
|
|
338
342
|
|
|
339
|
-
def __init__(self, dir_path:
|
|
343
|
+
def __init__(self, dir_path: str | Path):
|
|
340
344
|
"""Initialize folder base."""
|
|
341
345
|
self.dir_path = Path(dir_path)
|
|
342
346
|
|
|
@@ -374,11 +378,11 @@ class AFolder(IFolder, ABC):
|
|
|
374
378
|
"""Check if directory is empty."""
|
|
375
379
|
return AFolder.is_empty_static(self.dir_path)
|
|
376
380
|
|
|
377
|
-
def copy_to(self, destination:
|
|
381
|
+
def copy_to(self, destination: str | Path) -> bool:
|
|
378
382
|
"""Copy directory to destination."""
|
|
379
383
|
return AFolder.copy_dir(self.dir_path, destination)
|
|
380
384
|
|
|
381
|
-
def move_to(self, destination:
|
|
385
|
+
def move_to(self, destination: str | Path) -> bool:
|
|
382
386
|
"""Move directory to destination."""
|
|
383
387
|
return AFolder.move_dir(self.dir_path, destination)
|
|
384
388
|
|
|
@@ -387,12 +391,12 @@ class AFolder(IFolder, ABC):
|
|
|
387
391
|
# ============================================================================
|
|
388
392
|
|
|
389
393
|
@staticmethod
|
|
390
|
-
def exists(path:
|
|
394
|
+
def exists(path: str | Path) -> bool:
|
|
391
395
|
"""Check if directory exists."""
|
|
392
396
|
return Path(path).exists() and Path(path).is_dir()
|
|
393
397
|
|
|
394
398
|
@staticmethod
|
|
395
|
-
def create_dir(path:
|
|
399
|
+
def create_dir(path: str | Path, parents: bool = True, exist_ok: bool = True) -> bool:
|
|
396
400
|
"""Create directory."""
|
|
397
401
|
try:
|
|
398
402
|
Path(path).mkdir(parents=parents, exist_ok=exist_ok)
|
|
@@ -401,7 +405,7 @@ class AFolder(IFolder, ABC):
|
|
|
401
405
|
return False
|
|
402
406
|
|
|
403
407
|
@staticmethod
|
|
404
|
-
def delete_dir(path:
|
|
408
|
+
def delete_dir(path: str | Path, recursive: bool = False) -> bool:
|
|
405
409
|
"""Delete directory."""
|
|
406
410
|
try:
|
|
407
411
|
if recursive:
|
|
@@ -414,7 +418,7 @@ class AFolder(IFolder, ABC):
|
|
|
414
418
|
return False
|
|
415
419
|
|
|
416
420
|
@staticmethod
|
|
417
|
-
def list_files_static(path:
|
|
421
|
+
def list_files_static(path: str | Path, pattern: Optional[str] = None, recursive: bool = False) -> list[Path]:
|
|
418
422
|
"""List files in directory."""
|
|
419
423
|
if not AFolder.exists(path):
|
|
420
424
|
return []
|
|
@@ -431,7 +435,7 @@ class AFolder(IFolder, ABC):
|
|
|
431
435
|
return [p for p in Path(path).iterdir() if p.is_file()]
|
|
432
436
|
|
|
433
437
|
@staticmethod
|
|
434
|
-
def list_directories_static(path:
|
|
438
|
+
def list_directories_static(path: str | Path, recursive: bool = False) -> list[Path]:
|
|
435
439
|
"""List subdirectories."""
|
|
436
440
|
if not AFolder.exists(path):
|
|
437
441
|
return []
|
|
@@ -442,7 +446,7 @@ class AFolder(IFolder, ABC):
|
|
|
442
446
|
return [p for p in Path(path).iterdir() if p.is_dir()]
|
|
443
447
|
|
|
444
448
|
@staticmethod
|
|
445
|
-
def walk_static(path:
|
|
449
|
+
def walk_static(path: str | Path) -> list[tuple[Path, list[str], list[str]]]:
|
|
446
450
|
"""Walk directory tree."""
|
|
447
451
|
if not AFolder.exists(path):
|
|
448
452
|
return []
|
|
@@ -453,7 +457,7 @@ class AFolder(IFolder, ABC):
|
|
|
453
457
|
return result
|
|
454
458
|
|
|
455
459
|
@staticmethod
|
|
456
|
-
def get_size_static(path:
|
|
460
|
+
def get_size_static(path: str | Path) -> int:
|
|
457
461
|
"""Get directory size."""
|
|
458
462
|
if not AFolder.exists(path):
|
|
459
463
|
return 0
|
|
@@ -468,7 +472,7 @@ class AFolder(IFolder, ABC):
|
|
|
468
472
|
return total_size
|
|
469
473
|
|
|
470
474
|
@staticmethod
|
|
471
|
-
def is_empty_static(path:
|
|
475
|
+
def is_empty_static(path: str | Path) -> bool:
|
|
472
476
|
"""Check if directory is empty."""
|
|
473
477
|
if not AFolder.exists(path):
|
|
474
478
|
return True
|
|
@@ -479,7 +483,7 @@ class AFolder(IFolder, ABC):
|
|
|
479
483
|
return True
|
|
480
484
|
|
|
481
485
|
@staticmethod
|
|
482
|
-
def copy_dir(source:
|
|
486
|
+
def copy_dir(source: str | Path, destination: str | Path) -> bool:
|
|
483
487
|
"""Copy directory."""
|
|
484
488
|
try:
|
|
485
489
|
import shutil
|
|
@@ -489,7 +493,7 @@ class AFolder(IFolder, ABC):
|
|
|
489
493
|
return False
|
|
490
494
|
|
|
491
495
|
@staticmethod
|
|
492
|
-
def move_dir(source:
|
|
496
|
+
def move_dir(source: str | Path, destination: str | Path) -> bool:
|
|
493
497
|
"""Move directory."""
|
|
494
498
|
try:
|
|
495
499
|
import shutil
|
|
@@ -499,24 +503,24 @@ class AFolder(IFolder, ABC):
|
|
|
499
503
|
return False
|
|
500
504
|
|
|
501
505
|
@staticmethod
|
|
502
|
-
def get_permissions(path:
|
|
506
|
+
def get_permissions(path: str | Path) -> int:
|
|
503
507
|
"""Get directory permissions."""
|
|
504
508
|
if AFolder.exists(path):
|
|
505
509
|
return Path(path).stat().st_mode
|
|
506
510
|
return 0
|
|
507
511
|
|
|
508
512
|
@staticmethod
|
|
509
|
-
def is_readable(path:
|
|
513
|
+
def is_readable(path: str | Path) -> bool:
|
|
510
514
|
"""Check if directory is readable."""
|
|
511
515
|
return AFolder.exists(path) and os.access(path, os.R_OK)
|
|
512
516
|
|
|
513
517
|
@staticmethod
|
|
514
|
-
def is_writable(path:
|
|
518
|
+
def is_writable(path: str | Path) -> bool:
|
|
515
519
|
"""Check if directory is writable."""
|
|
516
520
|
return AFolder.exists(path) and os.access(path, os.W_OK)
|
|
517
521
|
|
|
518
522
|
@staticmethod
|
|
519
|
-
def is_executable(path:
|
|
523
|
+
def is_executable(path: str | Path) -> bool:
|
|
520
524
|
"""Check if directory is executable."""
|
|
521
525
|
return AFolder.exists(path) and os.access(path, os.X_OK)
|
|
522
526
|
|
|
@@ -533,85 +537,85 @@ class APath(IPath, ABC):
|
|
|
533
537
|
# ============================================================================
|
|
534
538
|
|
|
535
539
|
@staticmethod
|
|
536
|
-
def normalize(path:
|
|
540
|
+
def normalize(path: str | Path) -> Path:
|
|
537
541
|
"""Normalize path."""
|
|
538
542
|
return Path(path).resolve()
|
|
539
543
|
|
|
540
544
|
@staticmethod
|
|
541
|
-
def resolve(path:
|
|
545
|
+
def resolve(path: str | Path) -> Path:
|
|
542
546
|
"""Resolve path."""
|
|
543
547
|
return Path(path).resolve()
|
|
544
548
|
|
|
545
549
|
@staticmethod
|
|
546
|
-
def absolute(path:
|
|
550
|
+
def absolute(path: str | Path) -> Path:
|
|
547
551
|
"""Get absolute path."""
|
|
548
552
|
return Path(path).absolute()
|
|
549
553
|
|
|
550
554
|
@staticmethod
|
|
551
|
-
def relative(path:
|
|
555
|
+
def relative(path: str | Path, start: Optional[str | Path] = None) -> Path:
|
|
552
556
|
"""Get relative path."""
|
|
553
557
|
if start is None:
|
|
554
558
|
start = Path.cwd()
|
|
555
559
|
return Path(path).relative_to(Path(start))
|
|
556
560
|
|
|
557
561
|
@staticmethod
|
|
558
|
-
def join(*paths:
|
|
562
|
+
def join(*paths: str | Path) -> Path:
|
|
559
563
|
"""Join paths."""
|
|
560
564
|
return Path(*paths)
|
|
561
565
|
|
|
562
566
|
@staticmethod
|
|
563
|
-
def split(path:
|
|
567
|
+
def split(path: str | Path) -> tuple[Path, str]:
|
|
564
568
|
"""Split path into directory and filename."""
|
|
565
569
|
p = Path(path)
|
|
566
570
|
return p.parent, p.name
|
|
567
571
|
|
|
568
572
|
@staticmethod
|
|
569
|
-
def get_extension(path:
|
|
573
|
+
def get_extension(path: str | Path) -> str:
|
|
570
574
|
"""Get file extension."""
|
|
571
575
|
return Path(path).suffix
|
|
572
576
|
|
|
573
577
|
@staticmethod
|
|
574
|
-
def get_stem(path:
|
|
578
|
+
def get_stem(path: str | Path) -> str:
|
|
575
579
|
"""Get file stem (name without extension)."""
|
|
576
580
|
return Path(path).stem
|
|
577
581
|
|
|
578
582
|
@staticmethod
|
|
579
|
-
def get_name(path:
|
|
583
|
+
def get_name(path: str | Path) -> str:
|
|
580
584
|
"""Get file/directory name."""
|
|
581
585
|
return Path(path).name
|
|
582
586
|
|
|
583
587
|
@staticmethod
|
|
584
|
-
def get_parent(path:
|
|
588
|
+
def get_parent(path: str | Path) -> Path:
|
|
585
589
|
"""Get parent directory."""
|
|
586
590
|
return Path(path).parent
|
|
587
591
|
|
|
588
592
|
@staticmethod
|
|
589
|
-
def is_absolute(path:
|
|
593
|
+
def is_absolute(path: str | Path) -> bool:
|
|
590
594
|
"""Check if path is absolute."""
|
|
591
595
|
return Path(path).is_absolute()
|
|
592
596
|
|
|
593
597
|
@staticmethod
|
|
594
|
-
def is_relative(path:
|
|
598
|
+
def is_relative(path: str | Path) -> bool:
|
|
595
599
|
"""Check if path is relative."""
|
|
596
600
|
return not Path(path).is_absolute()
|
|
597
601
|
|
|
598
602
|
@staticmethod
|
|
599
|
-
def get_parts(path:
|
|
603
|
+
def get_parts(path: str | Path) -> tuple:
|
|
600
604
|
"""Get path parts."""
|
|
601
605
|
return Path(path).parts
|
|
602
606
|
|
|
603
607
|
@staticmethod
|
|
604
|
-
def match(path:
|
|
608
|
+
def match(path: str | Path, pattern: str) -> bool:
|
|
605
609
|
"""Check if path matches pattern."""
|
|
606
610
|
return Path(path).match(pattern)
|
|
607
611
|
|
|
608
612
|
@staticmethod
|
|
609
|
-
def with_suffix(path:
|
|
613
|
+
def with_suffix(path: str | Path, suffix: str) -> Path:
|
|
610
614
|
"""Get path with new suffix."""
|
|
611
615
|
return Path(path).with_suffix(suffix)
|
|
612
616
|
|
|
613
617
|
@staticmethod
|
|
614
|
-
def with_name(path:
|
|
618
|
+
def with_name(path: str | Path, name: str) -> Path:
|
|
615
619
|
"""Get path with new name."""
|
|
616
620
|
return Path(path).with_name(name)
|
|
617
621
|
|
|
@@ -627,19 +631,19 @@ class AStream(IStream, ABC):
|
|
|
627
631
|
"""Initialize stream base."""
|
|
628
632
|
self._closed = False
|
|
629
633
|
self._position = 0
|
|
630
|
-
self._stream: Optional[
|
|
634
|
+
self._stream: Optional[TextIO | BinaryIO] = None
|
|
631
635
|
|
|
632
636
|
# ============================================================================
|
|
633
637
|
# INSTANCE METHODS
|
|
634
638
|
# ============================================================================
|
|
635
639
|
|
|
636
640
|
@abstractmethod
|
|
637
|
-
def read(self, size: Optional[int] = None) ->
|
|
641
|
+
def read(self, size: Optional[int] = None) -> str | bytes:
|
|
638
642
|
"""Read from stream."""
|
|
639
643
|
pass
|
|
640
644
|
|
|
641
645
|
@abstractmethod
|
|
642
|
-
def write(self, data:
|
|
646
|
+
def write(self, data: str | bytes) -> int:
|
|
643
647
|
"""Write to stream."""
|
|
644
648
|
pass
|
|
645
649
|
|
|
@@ -670,27 +674,27 @@ class AStream(IStream, ABC):
|
|
|
670
674
|
# ============================================================================
|
|
671
675
|
|
|
672
676
|
@staticmethod
|
|
673
|
-
def open_file(path:
|
|
677
|
+
def open_file(path: str | Path, mode: str = 'r', encoding: Optional[str] = None) -> TextIO | BinaryIO:
|
|
674
678
|
"""Open file as stream."""
|
|
675
679
|
return open(path, mode, encoding=encoding)
|
|
676
680
|
|
|
677
681
|
@staticmethod
|
|
678
|
-
def is_closed(stream:
|
|
682
|
+
def is_closed(stream: TextIO | BinaryIO) -> bool:
|
|
679
683
|
"""Check if stream is closed."""
|
|
680
684
|
return stream.closed
|
|
681
685
|
|
|
682
686
|
@staticmethod
|
|
683
|
-
def readable(stream:
|
|
687
|
+
def readable(stream: TextIO | BinaryIO) -> bool:
|
|
684
688
|
"""Check if stream is readable."""
|
|
685
689
|
return hasattr(stream, 'readable') and stream.readable()
|
|
686
690
|
|
|
687
691
|
@staticmethod
|
|
688
|
-
def writable(stream:
|
|
692
|
+
def writable(stream: TextIO | BinaryIO) -> bool:
|
|
689
693
|
"""Check if stream is writable."""
|
|
690
694
|
return hasattr(stream, 'writable') and stream.writable()
|
|
691
695
|
|
|
692
696
|
@staticmethod
|
|
693
|
-
def seekable(stream:
|
|
697
|
+
def seekable(stream: TextIO | BinaryIO) -> bool:
|
|
694
698
|
"""Check if stream is seekable."""
|
|
695
699
|
return hasattr(stream, 'seekable') and stream.seekable()
|
|
696
700
|
|
|
@@ -713,12 +717,12 @@ class AAsyncIO(IAsyncIO, ABC):
|
|
|
713
717
|
# ============================================================================
|
|
714
718
|
|
|
715
719
|
@abstractmethod
|
|
716
|
-
async def aread(self, size: Optional[int] = None) ->
|
|
720
|
+
async def aread(self, size: Optional[int] = None) -> str | bytes:
|
|
717
721
|
"""Async read operation."""
|
|
718
722
|
pass
|
|
719
723
|
|
|
720
724
|
@abstractmethod
|
|
721
|
-
async def awrite(self, data:
|
|
725
|
+
async def awrite(self, data: str | bytes) -> int:
|
|
722
726
|
"""Async write operation."""
|
|
723
727
|
pass
|
|
724
728
|
|
|
@@ -749,14 +753,14 @@ class AAsyncIO(IAsyncIO, ABC):
|
|
|
749
753
|
# ============================================================================
|
|
750
754
|
|
|
751
755
|
@staticmethod
|
|
752
|
-
async def aopen_file(path:
|
|
756
|
+
async def aopen_file(path: str | Path, mode: str = 'r', encoding: Optional[str] = None) -> Any:
|
|
753
757
|
"""Async open file."""
|
|
754
758
|
# Lazy installation system will handle aiofiles if missing
|
|
755
759
|
import aiofiles
|
|
756
760
|
return await aiofiles.open(path, mode, encoding=encoding)
|
|
757
761
|
|
|
758
762
|
@staticmethod
|
|
759
|
-
async def aread_text(path:
|
|
763
|
+
async def aread_text(path: str | Path, encoding: str = 'utf-8') -> str:
|
|
760
764
|
"""Async read text file."""
|
|
761
765
|
# Lazy installation system will handle aiofiles if missing
|
|
762
766
|
import aiofiles
|
|
@@ -764,7 +768,7 @@ class AAsyncIO(IAsyncIO, ABC):
|
|
|
764
768
|
return await f.read()
|
|
765
769
|
|
|
766
770
|
@staticmethod
|
|
767
|
-
async def aread_bytes(path:
|
|
771
|
+
async def aread_bytes(path: str | Path) -> bytes:
|
|
768
772
|
"""Async read binary file."""
|
|
769
773
|
# Lazy installation system will handle aiofiles if missing
|
|
770
774
|
import aiofiles
|
|
@@ -772,7 +776,7 @@ class AAsyncIO(IAsyncIO, ABC):
|
|
|
772
776
|
return await f.read()
|
|
773
777
|
|
|
774
778
|
@staticmethod
|
|
775
|
-
async def awrite_text(path:
|
|
779
|
+
async def awrite_text(path: str | Path, content: str, encoding: str = 'utf-8') -> bool:
|
|
776
780
|
"""Async write text to file."""
|
|
777
781
|
try:
|
|
778
782
|
# Lazy installation system will handle aiofiles if missing
|
|
@@ -785,7 +789,7 @@ class AAsyncIO(IAsyncIO, ABC):
|
|
|
785
789
|
return False
|
|
786
790
|
|
|
787
791
|
@staticmethod
|
|
788
|
-
async def awrite_bytes(path:
|
|
792
|
+
async def awrite_bytes(path: str | Path, content: bytes) -> bool:
|
|
789
793
|
"""Async write bytes to file."""
|
|
790
794
|
try:
|
|
791
795
|
# Lazy installation system will handle aiofiles if missing
|
|
@@ -815,28 +819,28 @@ class AAtomicOperations(IAtomicOperations, ABC):
|
|
|
815
819
|
# ============================================================================
|
|
816
820
|
|
|
817
821
|
@abstractmethod
|
|
818
|
-
def atomic_write(self, file_path:
|
|
822
|
+
def atomic_write(self, file_path: str | Path, data: str | bytes,
|
|
819
823
|
backup: bool = True) -> OperationResult:
|
|
820
824
|
"""Atomically write data to file."""
|
|
821
825
|
pass
|
|
822
826
|
|
|
823
827
|
@abstractmethod
|
|
824
|
-
def atomic_copy(self, source:
|
|
828
|
+
def atomic_copy(self, source: str | Path, destination: str | Path) -> OperationResult:
|
|
825
829
|
"""Atomically copy file."""
|
|
826
830
|
pass
|
|
827
831
|
|
|
828
832
|
@abstractmethod
|
|
829
|
-
def atomic_move(self, source:
|
|
833
|
+
def atomic_move(self, source: str | Path, destination: str | Path) -> OperationResult:
|
|
830
834
|
"""Atomically move file."""
|
|
831
835
|
pass
|
|
832
836
|
|
|
833
837
|
@abstractmethod
|
|
834
|
-
def atomic_delete(self, file_path:
|
|
838
|
+
def atomic_delete(self, file_path: str | Path, backup: bool = True) -> OperationResult:
|
|
835
839
|
"""Atomically delete file."""
|
|
836
840
|
pass
|
|
837
841
|
|
|
838
842
|
@abstractmethod
|
|
839
|
-
def atomic_rename(self, old_path:
|
|
843
|
+
def atomic_rename(self, old_path: str | Path, new_path: str | Path) -> OperationResult:
|
|
840
844
|
"""Atomically rename file."""
|
|
841
845
|
pass
|
|
842
846
|
|
|
@@ -845,7 +849,7 @@ class AAtomicOperations(IAtomicOperations, ABC):
|
|
|
845
849
|
# ============================================================================
|
|
846
850
|
|
|
847
851
|
@staticmethod
|
|
848
|
-
def atomic_write_static(file_path:
|
|
852
|
+
def atomic_write_static(file_path: str | Path, data: str | bytes,
|
|
849
853
|
backup: bool = True) -> OperationResult:
|
|
850
854
|
"""Atomically write data to file."""
|
|
851
855
|
try:
|
|
@@ -860,7 +864,7 @@ class AAtomicOperations(IAtomicOperations, ABC):
|
|
|
860
864
|
return OperationResult.FAILED
|
|
861
865
|
|
|
862
866
|
@staticmethod
|
|
863
|
-
def atomic_copy_static(source:
|
|
867
|
+
def atomic_copy_static(source: str | Path, destination: str | Path) -> OperationResult:
|
|
864
868
|
"""Atomically copy file."""
|
|
865
869
|
try:
|
|
866
870
|
from .atomic_file import AtomicFileWriter
|
|
@@ -873,7 +877,7 @@ class AAtomicOperations(IAtomicOperations, ABC):
|
|
|
873
877
|
return OperationResult.FAILED
|
|
874
878
|
|
|
875
879
|
@staticmethod
|
|
876
|
-
def atomic_move_static(source:
|
|
880
|
+
def atomic_move_static(source: str | Path, destination: str | Path) -> OperationResult:
|
|
877
881
|
"""Atomically move file."""
|
|
878
882
|
try:
|
|
879
883
|
from .atomic_file import AtomicFileWriter
|
|
@@ -891,7 +895,7 @@ class AAtomicOperations(IAtomicOperations, ABC):
|
|
|
891
895
|
return OperationResult.FAILED
|
|
892
896
|
|
|
893
897
|
@staticmethod
|
|
894
|
-
def atomic_delete_static(file_path:
|
|
898
|
+
def atomic_delete_static(file_path: str | Path, backup: bool = True) -> OperationResult:
|
|
895
899
|
"""Atomically delete file."""
|
|
896
900
|
try:
|
|
897
901
|
if backup and Path(file_path).exists():
|
|
@@ -905,7 +909,7 @@ class AAtomicOperations(IAtomicOperations, ABC):
|
|
|
905
909
|
return OperationResult.FAILED
|
|
906
910
|
|
|
907
911
|
@staticmethod
|
|
908
|
-
def atomic_rename_static(old_path:
|
|
912
|
+
def atomic_rename_static(old_path: str | Path, new_path: str | Path) -> OperationResult:
|
|
909
913
|
"""Atomically rename file."""
|
|
910
914
|
return AAtomicOperations.atomic_move_static(old_path, new_path)
|
|
911
915
|
|
|
@@ -926,24 +930,24 @@ class ABackupOperations(IBackupOperations, ABC):
|
|
|
926
930
|
# ============================================================================
|
|
927
931
|
|
|
928
932
|
@abstractmethod
|
|
929
|
-
def create_backup(self, source:
|
|
933
|
+
def create_backup(self, source: str | Path, backup_dir: str | Path) -> Optional[Path]:
|
|
930
934
|
"""Create backup of file or directory."""
|
|
931
935
|
pass
|
|
932
936
|
|
|
933
937
|
@abstractmethod
|
|
934
|
-
def restore_backup(self, backup_path:
|
|
938
|
+
def restore_backup(self, backup_path: str | Path, target: str | Path) -> OperationResult:
|
|
935
939
|
"""Restore from backup."""
|
|
936
940
|
pass
|
|
937
941
|
|
|
938
|
-
def list_backups(self, backup_dir:
|
|
942
|
+
def list_backups(self, backup_dir: str | Path) -> list[Path]:
|
|
939
943
|
"""List available backups."""
|
|
940
944
|
return ABackupOperations.list_backups_static(backup_dir)
|
|
941
945
|
|
|
942
|
-
def cleanup_backups(self, backup_dir:
|
|
946
|
+
def cleanup_backups(self, backup_dir: str | Path, max_age_days: int = 30) -> int:
|
|
943
947
|
"""Cleanup old backups."""
|
|
944
948
|
return ABackupOperations.cleanup_backups_static(backup_dir, max_age_days)
|
|
945
949
|
|
|
946
|
-
def verify_backup(self, backup_path:
|
|
950
|
+
def verify_backup(self, backup_path: str | Path) -> bool:
|
|
947
951
|
"""Verify backup integrity."""
|
|
948
952
|
return ABackupOperations.verify_backup_static(backup_path)
|
|
949
953
|
|
|
@@ -952,7 +956,7 @@ class ABackupOperations(IBackupOperations, ABC):
|
|
|
952
956
|
# ============================================================================
|
|
953
957
|
|
|
954
958
|
@staticmethod
|
|
955
|
-
def create_backup_static(source:
|
|
959
|
+
def create_backup_static(source: str | Path, backup_dir: str | Path) -> Optional[Path]:
|
|
956
960
|
"""Create backup of file or directory."""
|
|
957
961
|
try:
|
|
958
962
|
source_path = Path(source)
|
|
@@ -976,7 +980,7 @@ class ABackupOperations(IBackupOperations, ABC):
|
|
|
976
980
|
return None
|
|
977
981
|
|
|
978
982
|
@staticmethod
|
|
979
|
-
def restore_backup_static(backup_path:
|
|
983
|
+
def restore_backup_static(backup_path: str | Path, target: str | Path) -> OperationResult:
|
|
980
984
|
"""Restore from backup."""
|
|
981
985
|
try:
|
|
982
986
|
backup = Path(backup_path)
|
|
@@ -1000,7 +1004,7 @@ class ABackupOperations(IBackupOperations, ABC):
|
|
|
1000
1004
|
return OperationResult.FAILED
|
|
1001
1005
|
|
|
1002
1006
|
@staticmethod
|
|
1003
|
-
def list_backups_static(backup_dir:
|
|
1007
|
+
def list_backups_static(backup_dir: str | Path) -> list[Path]:
|
|
1004
1008
|
"""List available backups."""
|
|
1005
1009
|
try:
|
|
1006
1010
|
backup_path = Path(backup_dir)
|
|
@@ -1012,7 +1016,7 @@ class ABackupOperations(IBackupOperations, ABC):
|
|
|
1012
1016
|
return []
|
|
1013
1017
|
|
|
1014
1018
|
@staticmethod
|
|
1015
|
-
def cleanup_backups_static(backup_dir:
|
|
1019
|
+
def cleanup_backups_static(backup_dir: str | Path, max_age_days: int = 30) -> int:
|
|
1016
1020
|
"""Cleanup old backups."""
|
|
1017
1021
|
try:
|
|
1018
1022
|
backup_path = Path(backup_dir)
|
|
@@ -1033,7 +1037,7 @@ class ABackupOperations(IBackupOperations, ABC):
|
|
|
1033
1037
|
return 0
|
|
1034
1038
|
|
|
1035
1039
|
@staticmethod
|
|
1036
|
-
def verify_backup_static(backup_path:
|
|
1040
|
+
def verify_backup_static(backup_path: str | Path) -> bool:
|
|
1037
1041
|
"""Verify backup integrity."""
|
|
1038
1042
|
try:
|
|
1039
1043
|
backup = Path(backup_path)
|
|
@@ -1069,7 +1073,7 @@ class ATemporaryOperations(ITemporaryOperations, ABC):
|
|
|
1069
1073
|
"""Create temporary directory."""
|
|
1070
1074
|
pass
|
|
1071
1075
|
|
|
1072
|
-
def cleanup_temp(self, path:
|
|
1076
|
+
def cleanup_temp(self, path: str | Path) -> bool:
|
|
1073
1077
|
"""Cleanup temporary file or directory."""
|
|
1074
1078
|
return ATemporaryOperations.cleanup_temp_static(path)
|
|
1075
1079
|
|
|
@@ -1107,7 +1111,7 @@ class ATemporaryOperations(ITemporaryOperations, ABC):
|
|
|
1107
1111
|
return Path(temp_path)
|
|
1108
1112
|
|
|
1109
1113
|
@staticmethod
|
|
1110
|
-
def cleanup_temp_static(path:
|
|
1114
|
+
def cleanup_temp_static(path: str | Path) -> bool:
|
|
1111
1115
|
"""Cleanup temporary file or directory."""
|
|
1112
1116
|
try:
|
|
1113
1117
|
temp_path = Path(path)
|
|
@@ -1129,7 +1133,7 @@ class ATemporaryOperations(ITemporaryOperations, ABC):
|
|
|
1129
1133
|
return Path(tempfile.gettempdir())
|
|
1130
1134
|
|
|
1131
1135
|
@staticmethod
|
|
1132
|
-
def is_temp(path:
|
|
1136
|
+
def is_temp(path: str | Path) -> bool:
|
|
1133
1137
|
"""Check if path is temporary."""
|
|
1134
1138
|
temp_path = Path(path)
|
|
1135
1139
|
temp_base = ATemporaryOperations.get_temp_base_dir()
|
|
@@ -1160,7 +1164,7 @@ class AUnifiedIO(AFile, AFolder, APath, AStream, AAsyncIO, AAtomicOperations, AB
|
|
|
1160
1164
|
- xwsystem integration (security, validation, monitoring)
|
|
1161
1165
|
"""
|
|
1162
1166
|
|
|
1163
|
-
def __init__(self, file_path: Optional[
|
|
1167
|
+
def __init__(self, file_path: Optional[str | Path] = None, **config):
|
|
1164
1168
|
"""
|
|
1165
1169
|
Initialize unified I/O with xwsystem integration.
|
|
1166
1170
|
|
|
@@ -1225,7 +1229,7 @@ class AFileManager(AFile, AFolder, APath, AAtomicOperations, ABackupOperations,
|
|
|
1225
1229
|
- Format detection and intelligent handling
|
|
1226
1230
|
"""
|
|
1227
1231
|
|
|
1228
|
-
def __init__(self, base_path: Optional[
|
|
1232
|
+
def __init__(self, base_path: Optional[str | Path] = None, **config):
|
|
1229
1233
|
"""
|
|
1230
1234
|
Initialize file manager with xwsystem integration.
|
|
1231
1235
|
|
|
@@ -1268,7 +1272,7 @@ class AFileManager(AFile, AFolder, APath, AAtomicOperations, ABackupOperations,
|
|
|
1268
1272
|
if self.cleanup_temp_on_exit:
|
|
1269
1273
|
self.cleanup_all_temp()
|
|
1270
1274
|
|
|
1271
|
-
def detect_file_type(self, file_path:
|
|
1275
|
+
def detect_file_type(self, file_path: str | Path) -> str:
|
|
1272
1276
|
"""
|
|
1273
1277
|
Detect file type from extension and content.
|
|
1274
1278
|
|
|
@@ -1315,7 +1319,7 @@ class AFileManager(AFile, AFolder, APath, AAtomicOperations, ABackupOperations,
|
|
|
1315
1319
|
|
|
1316
1320
|
return type_mappings.get(ext, 'unknown')
|
|
1317
1321
|
|
|
1318
|
-
def get_file_info(self, file_path:
|
|
1322
|
+
def get_file_info(self, file_path: str | Path) -> dict[str, Any]:
|
|
1319
1323
|
"""
|
|
1320
1324
|
Get comprehensive file information.
|
|
1321
1325
|
|
|
@@ -1358,7 +1362,7 @@ class AFileManager(AFile, AFolder, APath, AAtomicOperations, ABackupOperations,
|
|
|
1358
1362
|
'error': str(e)
|
|
1359
1363
|
}
|
|
1360
1364
|
|
|
1361
|
-
def is_safe_to_process(self, file_path:
|
|
1365
|
+
def is_safe_to_process(self, file_path: str | Path) -> bool:
|
|
1362
1366
|
"""
|
|
1363
1367
|
Check if file is safe to process (not too large, accessible, etc.).
|
|
1364
1368
|
|