annet 3.10.0__tar.gz → 3.12.0__tar.gz
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.
Potentially problematic release.
This version of annet might be problematic. Click here for more details.
- {annet-3.10.0/annet.egg-info → annet-3.12.0}/PKG-INFO +1 -1
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/adapter.py +1 -1
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/query.py +16 -13
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/storage_base.py +2 -1
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/storage_opts.py +9 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v37/storage.py +3 -2
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v41/storage.py +3 -2
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v42/storage.py +3 -2
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/devdb/__init__.py +4 -2
- {annet-3.10.0 → annet-3.12.0}/annet/api/__init__.py +9 -1
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/aruba/ap_env.py +1 -1
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/huawei.rul +2 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/tabparser.py +48 -26
- {annet-3.10.0 → annet-3.12.0/annet.egg-info}/PKG-INFO +1 -1
- {annet-3.10.0 → annet-3.12.0}/annet.egg-info/SOURCES.txt +0 -1
- annet-3.10.0/annet/executor.py +0 -172
- {annet-3.10.0 → annet-3.12.0}/AUTHORS +0 -0
- {annet-3.10.0 → annet-3.12.0}/LICENSE +0 -0
- {annet-3.10.0 → annet-3.12.0}/MANIFEST.in +0 -0
- {annet-3.10.0 → annet-3.12.0}/README.md +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/fetchers/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/fetchers/stub/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/fetchers/stub/fetcher.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/file/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/file/provider.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/client.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/manufacturer.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/common/status_client.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/provider.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v24/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v24/models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v24/storage.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v37/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v37/models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v41/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v41/models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v42/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/adapters/netbox/v42/models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annet.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/command.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/diff.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/errors.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/filter_acl.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/jsontools.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/lib.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/db.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/devdb/data/devdb.json +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/views/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/views/dump.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/netdev/views/hardware.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/output.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/patching.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rbparser/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rbparser/acl.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rbparser/deploying.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rbparser/ordering.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rbparser/platform.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rbparser/syntax.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rulebook/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/rulebook/common.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/annlib/types.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/argparse.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/bgp_models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/cli.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/cli_args.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/configs/context.yml +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/configs/logging.yaml +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/connectors.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/deploy.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/deploy_ui.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/diff.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/filtering.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/gen.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/base.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/common/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/common/initial.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/entire.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/exceptions.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/jsonfragment.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/partial.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/perf.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/ref.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/generators/result.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/hardware.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/implicit.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/lib.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/basemodel.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/device_models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/executor.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/match_args.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/models_converter.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/peer_models.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/port_processor.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/mesh/registry.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/output.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/parallel.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/patching.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/reference.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/action.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/condition.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/match_builder.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/policy.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/result.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/routemap.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl/statement_builder.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/aspath.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/community.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/cumulus_frr.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/entities.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/execute.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/policy.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/prefix_lists.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rpl_generators/rd.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/arista/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/arista/aaa.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/arista/iface.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/aruba/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/aruba/misc.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/b4com/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/b4com/file.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/b4com/iface.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/cisco/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/cisco/iface.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/cisco/misc.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/cisco/vlandb.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/common.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/deploying.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/generic/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/generic/misc.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/huawei/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/huawei/aaa.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/huawei/bgp.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/huawei/iface.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/huawei/misc.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/huawei/vlandb.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/juniper/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/juniper/iface.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/nexus/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/nexus/iface.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/patching.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/routeros/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/routeros/file.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/arista.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/arista.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/arista.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/aruba.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/aruba.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/aruba.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/b4com.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/b4com.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/b4com.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/cisco.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/cisco.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/cisco.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/huawei.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/huawei.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/iosxr.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/iosxr.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/iosxr.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/juniper.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/juniper.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/nexus.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/nexus.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/nexus.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/nokia.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/optixtrans.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/optixtrans.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/optixtrans.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/pc.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/pc.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/pc.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/ribbon.deploy +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/ribbon.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/routeros.order +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/rulebook/texts/routeros.rul +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/storage.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/text_term_format.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/tracing.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/types.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/base.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/arista.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/aruba.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/b4com.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/cisco.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/h3c.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/huawei.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/iosxr.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/juniper.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/nexus.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/nokia.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/optixtrans.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/pc.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/ribbon.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/library/routeros.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet/vendors/registry.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet.egg-info/dependency_links.txt +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet.egg-info/entry_points.txt +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet.egg-info/requires.txt +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet.egg-info/top_level.txt +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/example/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/example/hostname.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/example/lldp.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/mesh_example/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/mesh_example/bgp.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/mesh_example/mesh_logic.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/rpl_example/__init__.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/rpl_example/generator.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/rpl_example/items.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/rpl_example/mesh.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/annet_generators/rpl_example/route_policy.py +0 -0
- {annet-3.10.0 → annet-3.12.0}/requirements.txt +0 -0
- {annet-3.10.0 → annet-3.12.0}/setup.cfg +0 -0
- {annet-3.10.0 → annet-3.12.0}/setup.py +0 -0
|
@@ -37,19 +37,7 @@ class NetboxQuery(Query):
|
|
|
37
37
|
return self.query
|
|
38
38
|
|
|
39
39
|
def parse_query(self) -> Filter:
|
|
40
|
-
query_groups =
|
|
41
|
-
for q in self.globs:
|
|
42
|
-
if FIELD_VALUE_SEPARATOR in q:
|
|
43
|
-
glob_type, param = q.split(FIELD_VALUE_SEPARATOR, 2)
|
|
44
|
-
if glob_type not in ALLOWED_GLOB_GROUPS:
|
|
45
|
-
raise Exception(f"unknown query type: '{glob_type}'")
|
|
46
|
-
if not param:
|
|
47
|
-
raise Exception(f"empty param for '{glob_type}'")
|
|
48
|
-
query_groups[glob_type].append(param)
|
|
49
|
-
else:
|
|
50
|
-
query_groups["name"].append(q)
|
|
51
|
-
|
|
52
|
-
query_groups.default_factory = None
|
|
40
|
+
query_groups = parse_query(self.globs)
|
|
53
41
|
return cast(Filter, query_groups)
|
|
54
42
|
|
|
55
43
|
def is_empty(self) -> bool:
|
|
@@ -62,3 +50,18 @@ class NetboxQuery(Query):
|
|
|
62
50
|
if FIELD_VALUE_SEPARATOR in q:
|
|
63
51
|
return False
|
|
64
52
|
return True
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def parse_query(query: list[str]) -> dict[str, list[str]]:
|
|
56
|
+
query_groups = defaultdict(list)
|
|
57
|
+
for q in query:
|
|
58
|
+
if FIELD_VALUE_SEPARATOR in q:
|
|
59
|
+
glob_type, param = q.split(FIELD_VALUE_SEPARATOR, 2)
|
|
60
|
+
if glob_type not in ALLOWED_GLOB_GROUPS:
|
|
61
|
+
raise Exception(f"unknown query type: '{glob_type}'")
|
|
62
|
+
if not param:
|
|
63
|
+
raise Exception(f"empty param for '{glob_type}'")
|
|
64
|
+
query_groups[glob_type].append(param)
|
|
65
|
+
else:
|
|
66
|
+
query_groups["name"].append(q)
|
|
67
|
+
return dict(query_groups)
|
|
@@ -54,6 +54,7 @@ class BaseNetboxStorage(
|
|
|
54
54
|
token = opts.token
|
|
55
55
|
threads = opts.threads
|
|
56
56
|
self.exact_host_filter = opts.exact_host_filter
|
|
57
|
+
self.all_hosts_filter = opts.all_hosts_filter
|
|
57
58
|
self.netbox = self._init_adapter(url=url, token=token, ssl_context=ctx, threads=threads)
|
|
58
59
|
self._all_fqdns: Optional[list[str]] = None
|
|
59
60
|
self._id_devices: dict[int, NetboxDeviceT] = {}
|
|
@@ -87,7 +88,7 @@ class BaseNetboxStorage(
|
|
|
87
88
|
|
|
88
89
|
def resolve_all_fdnds(self) -> list[str]:
|
|
89
90
|
if self._all_fqdns is None:
|
|
90
|
-
self._all_fqdns = self.netbox.
|
|
91
|
+
self._all_fqdns = self.netbox.list_fqdns(self.all_hosts_filter)
|
|
91
92
|
return self._all_fqdns
|
|
92
93
|
|
|
93
94
|
def make_devices(
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import os
|
|
2
2
|
from typing import Any, Optional
|
|
3
|
+
from .query import parse_query
|
|
3
4
|
|
|
4
5
|
DEFAULT_URL = "http://localhost"
|
|
5
6
|
|
|
@@ -12,17 +13,24 @@ class NetboxStorageOpts:
|
|
|
12
13
|
insecure: bool = False,
|
|
13
14
|
exact_host_filter: bool = False,
|
|
14
15
|
threads: int = 1,
|
|
16
|
+
all_hosts_filter: dict[str, list[str]] | None = None,
|
|
15
17
|
):
|
|
16
18
|
self.url = url
|
|
17
19
|
self.token = token
|
|
18
20
|
self.insecure = insecure
|
|
19
21
|
self.exact_host_filter = exact_host_filter
|
|
20
22
|
self.threads = threads
|
|
23
|
+
self.all_hosts_filter: dict[str, list[str]] = all_hosts_filter or {}
|
|
21
24
|
|
|
22
25
|
@classmethod
|
|
23
26
|
def parse_params(cls, conf_params: Optional[dict[str, str]], cli_opts: Any):
|
|
24
27
|
url = os.getenv("NETBOX_URL") or conf_params.get("url") or DEFAULT_URL
|
|
25
28
|
token = os.getenv("NETBOX_TOKEN", "").strip() or conf_params.get("token") or ""
|
|
29
|
+
all_hosts_filter = None
|
|
30
|
+
if all_hosts_filter_env := os.getenv("NETBOX_ALL_HOSTS_FILTER", "").strip():
|
|
31
|
+
all_hosts_filter = parse_query(all_hosts_filter_env.split(","))
|
|
32
|
+
elif all_hosts_filter_params := conf_params.get("all_hosts_filter"):
|
|
33
|
+
all_hosts_filter = all_hosts_filter_params
|
|
26
34
|
threads = os.getenv("NETBOX_CLIENT_THREADS", "").strip() or conf_params.get("threads") or "1"
|
|
27
35
|
insecure = False
|
|
28
36
|
if insecure_env := os.getenv("NETBOX_INSECURE", "").lower():
|
|
@@ -39,4 +47,5 @@ class NetboxStorageOpts:
|
|
|
39
47
|
insecure=insecure,
|
|
40
48
|
exact_host_filter=exact_host_filter,
|
|
41
49
|
threads=int(threads),
|
|
50
|
+
all_hosts_filter=all_hosts_filter,
|
|
42
51
|
)
|
|
@@ -70,10 +70,11 @@ class NetboxV37Adapter(NetboxAdapter[
|
|
|
70
70
|
list[FHRPGroupV37],
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
def
|
|
73
|
+
def list_fqdns(self, query: dict[str, list[str]] | None = None) -> list[str]:
|
|
74
|
+
query = query or {}
|
|
74
75
|
return [
|
|
75
76
|
d.name
|
|
76
|
-
for d in self.netbox.dcim_all_devices_brief().results
|
|
77
|
+
for d in self.netbox.dcim_all_devices_brief(**query).results
|
|
77
78
|
]
|
|
78
79
|
|
|
79
80
|
def list_devices(self, query: dict[str, list[str]]) -> list[NetboxDeviceV37]:
|
|
@@ -71,10 +71,11 @@ class NetboxV41Adapter(NetboxAdapter[
|
|
|
71
71
|
list[FHRPGroupV41],
|
|
72
72
|
)
|
|
73
73
|
|
|
74
|
-
def
|
|
74
|
+
def list_fqdns(self, query: dict[str, list[str]] | None = None) -> list[str]:
|
|
75
|
+
query = query or {}
|
|
75
76
|
return [
|
|
76
77
|
d.name
|
|
77
|
-
for d in self.netbox.dcim_all_devices_brief().results
|
|
78
|
+
for d in self.netbox.dcim_all_devices_brief(**query).results
|
|
78
79
|
]
|
|
79
80
|
|
|
80
81
|
def list_devices(self, query: dict[str, list[str]]) -> list[NetboxDeviceV41]:
|
|
@@ -72,10 +72,11 @@ class NetboxV42Adapter(NetboxAdapter[
|
|
|
72
72
|
list[FHRPGroupV41],
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
def
|
|
75
|
+
def list_fqdns(self, query: dict[str, list[str]] | None = None) -> list[str]:
|
|
76
|
+
query = query or {}
|
|
76
77
|
return [
|
|
77
78
|
d.name
|
|
78
|
-
for d in self.netbox.dcim_all_devices_brief().results
|
|
79
|
+
for d in self.netbox.dcim_all_devices_brief(**query).results
|
|
79
80
|
]
|
|
80
81
|
|
|
81
82
|
def list_devices(self, query: dict[str, list[str]]) -> list[NetboxDeviceV42]:
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
import json
|
|
3
3
|
import re
|
|
4
|
-
from
|
|
4
|
+
from pathlib import Path
|
|
5
5
|
from typing import Any, Dict
|
|
6
6
|
|
|
7
7
|
from annet.annlib.netdev.db import find_true_sequences, get_db
|
|
8
|
+
from annet.lib import get_context
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
@functools.lru_cache(None)
|
|
@@ -23,6 +24,7 @@ def _prepare_db() -> Dict[str, Any]:
|
|
|
23
24
|
from library.python import resource
|
|
24
25
|
raw = json.loads(resource.resfs_read("contrib/python/annet/annet/annlib/netdev/devdb/data/devdb.json").decode("utf-8"))
|
|
25
26
|
except ImportError:
|
|
26
|
-
|
|
27
|
+
devdb_file = Path(get_context().get("devdb", {}).get("path", Path(__file__).parent / "data" / "devdb.json"))
|
|
28
|
+
with devdb_file.open("r", encoding="utf-8") as f:
|
|
27
29
|
raw = json.load(f)
|
|
28
30
|
return {tuple(seq.split(".")): re.compile(regexp) for (seq, regexp) in raw.items()}
|
|
@@ -331,7 +331,15 @@ def diff(
|
|
|
331
331
|
loader: ann_gen.Loader,
|
|
332
332
|
device_ids: List[Any]
|
|
333
333
|
) -> tuple[Mapping[Device, Union[Diff, PCDiff]], Mapping[Device, Exception]]:
|
|
334
|
-
""" Сгенерировать
|
|
334
|
+
""" Сгенерировать дифф для устройств """
|
|
335
|
+
if args.config == "running":
|
|
336
|
+
fetcher = annet.deploy.get_fetcher()
|
|
337
|
+
ann_gen.live_configs = annet.lib.do_async(
|
|
338
|
+
fetcher.fetch(
|
|
339
|
+
[device for device in loader.devices if device.id in device_ids],
|
|
340
|
+
processes=args.parallel
|
|
341
|
+
)
|
|
342
|
+
)
|
|
335
343
|
stdin = args.stdin(filter_acl=args.filter_acl, config=None)
|
|
336
344
|
|
|
337
345
|
filterer = filtering.filterer_connector.get()
|
|
@@ -200,9 +200,11 @@ aaa
|
|
|
200
200
|
|
|
201
201
|
hwtacacs-server template *
|
|
202
202
|
hwtacacs-server shared-key
|
|
203
|
+
hwtacacs-server source-ip
|
|
203
204
|
|
|
204
205
|
hwtacacs server template *
|
|
205
206
|
hwtacacs server shared-key
|
|
207
|
+
hwtacacs server source-ip
|
|
206
208
|
|
|
207
209
|
!radius-server template default
|
|
208
210
|
|
|
@@ -64,8 +64,36 @@ class FormatterContext:
|
|
|
64
64
|
return self.next and self.next[0]
|
|
65
65
|
|
|
66
66
|
|
|
67
|
+
class NotUniquePatch:
|
|
68
|
+
def __init__(self):
|
|
69
|
+
"""In the case of comments, odict is not suitable: there may be several identical edit and exit"""
|
|
70
|
+
self._items = []
|
|
71
|
+
self._keys = set()
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, key, value):
|
|
74
|
+
self._keys.add(key)
|
|
75
|
+
self._items.append((key, value))
|
|
76
|
+
|
|
77
|
+
def keys(self):
|
|
78
|
+
return list(self)
|
|
79
|
+
|
|
80
|
+
def items(self):
|
|
81
|
+
return self._items
|
|
82
|
+
|
|
83
|
+
def __contains__(self, item):
|
|
84
|
+
return item in self._keys
|
|
85
|
+
|
|
86
|
+
def __iter__(self):
|
|
87
|
+
return iter(item[0] for item in self._items)
|
|
88
|
+
|
|
89
|
+
def __bool__(self) -> bool:
|
|
90
|
+
return bool(self._items)
|
|
91
|
+
|
|
92
|
+
|
|
67
93
|
# =====
|
|
68
94
|
class CommonFormatter:
|
|
95
|
+
cmd_path_cls = odict
|
|
96
|
+
|
|
69
97
|
def __init__(self, indent=" "):
|
|
70
98
|
self._indent = indent
|
|
71
99
|
self._block_begin = ""
|
|
@@ -96,7 +124,7 @@ class CommonFormatter:
|
|
|
96
124
|
)
|
|
97
125
|
|
|
98
126
|
def cmd_paths(self, patch: "PatchTree") -> odict:
|
|
99
|
-
ret =
|
|
127
|
+
ret = self.cmd_path_cls()
|
|
100
128
|
path = []
|
|
101
129
|
for row, context in self.blocks_and_context(patch, is_patch=True):
|
|
102
130
|
if row is BlockBegin:
|
|
@@ -222,7 +250,20 @@ class BlockExitFormatter(CommonFormatter):
|
|
|
222
250
|
yield exit_statement, last_row_context
|
|
223
251
|
|
|
224
252
|
|
|
253
|
+
class HuaweiPatch(NotUniquePatch):
|
|
254
|
+
policy_end_blocks = ("end-list", "endif", "end-filter")
|
|
255
|
+
|
|
256
|
+
def __setitem__(self, key, value):
|
|
257
|
+
if not key:
|
|
258
|
+
return
|
|
259
|
+
|
|
260
|
+
if key not in self or (key[0].startswith("xpl") and key[-1] in self.policy_end_blocks):
|
|
261
|
+
super().__setitem__(key, value)
|
|
262
|
+
|
|
263
|
+
|
|
225
264
|
class HuaweiFormatter(BlockExitFormatter):
|
|
265
|
+
cmd_path_cls = HuaweiPatch
|
|
266
|
+
|
|
226
267
|
def __init__(self, indent=" "):
|
|
227
268
|
super().__init__(
|
|
228
269
|
block_exit="quit",
|
|
@@ -237,9 +278,8 @@ class HuaweiFormatter(BlockExitFormatter):
|
|
|
237
278
|
def split(self, text):
|
|
238
279
|
# на старых прошивка наблюдается баг с двумя пробелами в этом месте в конфиге
|
|
239
280
|
# например на VRP V100R006C00SPC500 + V100R006SPH003
|
|
240
|
-
policy_end_blocks = ("end-list", "endif", "end-filter")
|
|
241
281
|
tree = self.split_remove_spaces(text)
|
|
242
|
-
tree[:] = filter(lambda x: not str(x).strip().startswith(policy_end_blocks), tree)
|
|
282
|
+
tree[:] = filter(lambda x: not str(x).strip().startswith(HuaweiPatch.policy_end_blocks), tree)
|
|
243
283
|
return tree
|
|
244
284
|
|
|
245
285
|
def block_exit(self, context: Optional[FormatterContext]):
|
|
@@ -256,7 +296,9 @@ class HuaweiFormatter(BlockExitFormatter):
|
|
|
256
296
|
return
|
|
257
297
|
|
|
258
298
|
if parent_row.startswith("xpl route-filter"):
|
|
259
|
-
if (row.startswith(("if", "elseif")) and row.endswith("then")) and
|
|
299
|
+
if (row.startswith(("if", "elseif")) and row.endswith("then")) and (
|
|
300
|
+
not row_next or not row_next.startswith(("elseif", "else"))
|
|
301
|
+
):
|
|
260
302
|
yield "endif"
|
|
261
303
|
elif row == "else":
|
|
262
304
|
yield "endif"
|
|
@@ -407,29 +449,9 @@ class AsrFormatter(BlockExitFormatter):
|
|
|
407
449
|
yield from super().block_exit(context)
|
|
408
450
|
|
|
409
451
|
|
|
410
|
-
class JuniperPatch:
|
|
411
|
-
def __init__(self):
|
|
412
|
-
"""In the case of comments, odict is not suitable: there may be several identical edit and exit"""
|
|
413
|
-
self._items = []
|
|
414
|
-
|
|
415
|
-
def __setitem__(self, key, value):
|
|
416
|
-
self._items.append((key, value))
|
|
417
|
-
|
|
418
|
-
def keys(self):
|
|
419
|
-
return list(self)
|
|
420
|
-
|
|
421
|
-
def items(self):
|
|
422
|
-
return self._items
|
|
423
|
-
|
|
424
|
-
def __iter__(self):
|
|
425
|
-
return iter(item[0] for item in self._items)
|
|
426
|
-
|
|
427
|
-
def __bool__(self) -> bool:
|
|
428
|
-
return bool(self._items)
|
|
429
|
-
|
|
430
|
-
|
|
431
452
|
class JuniperFormatter(CommonFormatter):
|
|
432
453
|
patch_set_prefix = "set"
|
|
454
|
+
cmd_path_cls = NotUniquePatch
|
|
433
455
|
|
|
434
456
|
@dataclasses.dataclass
|
|
435
457
|
class Comment:
|
|
@@ -526,7 +548,7 @@ class JuniperFormatter(CommonFormatter):
|
|
|
526
548
|
yield line + self._statement_end
|
|
527
549
|
|
|
528
550
|
def cmd_paths(self, patch, _prev=tuple()):
|
|
529
|
-
commands =
|
|
551
|
+
commands = self.cmd_path_cls()
|
|
530
552
|
|
|
531
553
|
for item in patch.itms:
|
|
532
554
|
key, childs, context = item.row, item.child, item.context
|
annet-3.10.0/annet/executor.py
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import os
|
|
3
|
-
import statistics
|
|
4
|
-
from abc import ABC, abstractmethod
|
|
5
|
-
from functools import partial
|
|
6
|
-
from operator import itemgetter
|
|
7
|
-
from typing import Any, List, Optional
|
|
8
|
-
|
|
9
|
-
import colorama
|
|
10
|
-
from annet.annlib.command import Command, CommandList, Question # noqa: F401
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class CommandResult(ABC):
|
|
14
|
-
@abstractmethod
|
|
15
|
-
def get_out(self) -> str:
|
|
16
|
-
pass
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class ExecutorException(Exception):
|
|
20
|
-
def __init__(self, *args: List[Any], auxiliary: Optional[Any] = None, **kwargs: object):
|
|
21
|
-
self.auxiliary = auxiliary
|
|
22
|
-
super().__init__(*args, **kwargs)
|
|
23
|
-
|
|
24
|
-
def __repr__(self) -> str:
|
|
25
|
-
return "%s(args=%r,auxiliary=%s)" % (self.__class__.__name__, self.args, self.auxiliary)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class ExecException(ExecutorException):
|
|
29
|
-
def __init__(self, msg: str, cmd: str, res: str, **kwargs):
|
|
30
|
-
super().__init__(**kwargs)
|
|
31
|
-
self.args = msg, cmd, res
|
|
32
|
-
self.kwargs = kwargs
|
|
33
|
-
self.msg = msg
|
|
34
|
-
self.cmd = cmd
|
|
35
|
-
self.res = res
|
|
36
|
-
|
|
37
|
-
def __str__(self) -> str:
|
|
38
|
-
return str(self.msg)
|
|
39
|
-
|
|
40
|
-
def __repr__(self) -> str:
|
|
41
|
-
return "%s<%s, %s>" % (self.__class__.__name__, self.msg, self.cmd)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class BadCommand(ExecException):
|
|
45
|
-
pass
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
class NonzeroRetcode(ExecException):
|
|
49
|
-
pass
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
class CommitException(ExecException):
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def _show_type_summary(caption, items, total, stat_items=None):
|
|
57
|
-
if items:
|
|
58
|
-
if not stat_items:
|
|
59
|
-
stat = ""
|
|
60
|
-
else:
|
|
61
|
-
avg = statistics.mean(stat_items)
|
|
62
|
-
stat = " %(min).1f/%(max).1f/%(avg).1f/%(stdev)s (min/max/avg/stdev)" % dict(
|
|
63
|
-
min=min(stat_items),
|
|
64
|
-
max=max(stat_items),
|
|
65
|
-
avg=avg,
|
|
66
|
-
stdev="-" if len(stat_items) < 2 else "%.1f" % statistics.stdev(stat_items, xbar=avg)
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
print("%-8s %d of %d%s" % (caption, len(items), total, stat))
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
def show_bulk_report(hostnames, res, durations, log_dir):
|
|
73
|
-
total = len(hostnames)
|
|
74
|
-
if not total:
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
colorama.init()
|
|
78
|
-
|
|
79
|
-
print("\n====== bulk deploy report ======")
|
|
80
|
-
|
|
81
|
-
done = [host for (host, hres) in res.items() if not isinstance(hres, Exception)]
|
|
82
|
-
cancelled = [host for (host, hres) in res.items() if isinstance(hres, asyncio.CancelledError)]
|
|
83
|
-
failed = [host for (host, hres) in res.items() if isinstance(hres, Exception) and host not in cancelled]
|
|
84
|
-
lost = [host for host in hostnames if host not in res]
|
|
85
|
-
limit = 30
|
|
86
|
-
|
|
87
|
-
_show_type_summary("Done :", done, total, [durations[h] for h in done])
|
|
88
|
-
_print_limit(done, partial(_print_hostname, style=colorama.Fore.GREEN), limit, total)
|
|
89
|
-
|
|
90
|
-
_show_type_summary("Failed :", failed, total, [durations[h] for h in failed])
|
|
91
|
-
|
|
92
|
-
_print_limit(failed, partial(_print_failed, res=res), limit, total)
|
|
93
|
-
|
|
94
|
-
_show_type_summary("Cancelled :", cancelled, total, [durations[h] for h in cancelled if durations[h] is not None])
|
|
95
|
-
_print_limit(cancelled, partial(_print_hostname, style=colorama.Fore.RED), limit, total)
|
|
96
|
-
|
|
97
|
-
_show_type_summary("Lost :", lost, total)
|
|
98
|
-
_print_limit(lost, _print_hostname, limit, total)
|
|
99
|
-
|
|
100
|
-
err_limit = 5
|
|
101
|
-
if failed:
|
|
102
|
-
errs = {}
|
|
103
|
-
for hostname in failed:
|
|
104
|
-
fmt_err = _format_exc(res[hostname])
|
|
105
|
-
if fmt_err in errs:
|
|
106
|
-
errs[fmt_err] += 1
|
|
107
|
-
else:
|
|
108
|
-
errs[fmt_err] = 1
|
|
109
|
-
print("Top errors :")
|
|
110
|
-
for fmt_err, n in sorted(errs.items(), key=itemgetter(1), reverse=True)[:err_limit]:
|
|
111
|
-
print(" %-4d %s" % (n, fmt_err))
|
|
112
|
-
print("\n", end="")
|
|
113
|
-
|
|
114
|
-
if log_dir:
|
|
115
|
-
print("See deploy logs in %s/\n" % os.path.relpath(log_dir))
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def _format_exc(exc):
|
|
119
|
-
if isinstance(exc, ExecException):
|
|
120
|
-
cmd = str(exc.cmd)
|
|
121
|
-
if len(cmd) > 50:
|
|
122
|
-
cmd = cmd[:50] + "~.."
|
|
123
|
-
return "'%s', cmd '%s'" % (exc.msg, cmd)
|
|
124
|
-
elif isinstance(exc, ExecutorException):
|
|
125
|
-
return "%s%r" % (exc.__class__.__name__, exc.args) # исключить многословный auxiliary
|
|
126
|
-
else:
|
|
127
|
-
return repr(exc)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
def _print_hostname(host, style=None):
|
|
131
|
-
if style:
|
|
132
|
-
host = style + host + colorama.Style.RESET_ALL
|
|
133
|
-
print(" %s" % host)
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
def _print_limit(items, printer, limit, total, end="\n"):
|
|
137
|
-
if not items:
|
|
138
|
-
return
|
|
139
|
-
if len(items) > limit and len(items) > total * 0.7:
|
|
140
|
-
print(" ... %d hosts" % len(items))
|
|
141
|
-
for host in items[:limit]:
|
|
142
|
-
printer(host)
|
|
143
|
-
if len(items) > limit:
|
|
144
|
-
print(" ... %d more hosts" % (len(items) - limit))
|
|
145
|
-
|
|
146
|
-
print(end, end="")
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def _print_failed(host, res):
|
|
150
|
-
exc = res[host]
|
|
151
|
-
color = colorama.Fore.YELLOW if isinstance(exc, Warning) else colorama.Fore.RED
|
|
152
|
-
print(" %s - %s" % (color + host + colorama.Style.RESET_ALL, _format_exc(exc)))
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class DeferredFileWrite:
|
|
156
|
-
def __init__(self, file, mode="r"):
|
|
157
|
-
self._file = file
|
|
158
|
-
wrapper = {"w": "a", "wb": "ab"}
|
|
159
|
-
if mode in wrapper:
|
|
160
|
-
self._mode = wrapper[mode]
|
|
161
|
-
else:
|
|
162
|
-
raise Exception()
|
|
163
|
-
|
|
164
|
-
def write(self, data):
|
|
165
|
-
with open(self._file, self._mode, encoding="utf-8") as fh:
|
|
166
|
-
fh.write(data)
|
|
167
|
-
|
|
168
|
-
def close(self):
|
|
169
|
-
pass
|
|
170
|
-
|
|
171
|
-
def flush(self):
|
|
172
|
-
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|