flow.record 3.15.dev7__tar.gz → 3.15.dev8__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.
Files changed (81) hide show
  1. {flow.record-3.15.dev7/flow.record.egg-info → flow.record-3.15.dev8}/PKG-INFO +1 -1
  2. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/__init__.py +4 -0
  3. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/base.py +34 -3
  4. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/version.py +2 -2
  5. {flow.record-3.15.dev7 → flow.record-3.15.dev8/flow.record.egg-info}/PKG-INFO +1 -1
  6. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_record.py +65 -1
  7. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/COPYRIGHT +0 -0
  8. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/LICENSE +0 -0
  9. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/MANIFEST.in +0 -0
  10. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/README.md +0 -0
  11. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/filesystem.py +0 -0
  12. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/passivedns.py +0 -0
  13. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/records.json +0 -0
  14. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/examples/tcpconn.py +0 -0
  15. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/__init__.py +0 -0
  16. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/archive.py +0 -0
  17. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/avro.py +0 -0
  18. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/broker.py +0 -0
  19. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/csvfile.py +0 -0
  20. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/duckdb.py +0 -0
  21. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/elastic.py +0 -0
  22. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/jsonfile.py +0 -0
  23. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/line.py +0 -0
  24. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/mongo.py +0 -0
  25. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/split.py +0 -0
  26. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/splunk.py +0 -0
  27. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/sqlite.py +0 -0
  28. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/stream.py +0 -0
  29. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/text.py +0 -0
  30. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/adapter/xlsx.py +0 -0
  31. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/exceptions.py +0 -0
  32. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/__init__.py +0 -0
  33. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/credential.py +0 -0
  34. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/__init__.py +0 -0
  35. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/ip.py +0 -0
  36. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/ipv4.py +0 -0
  37. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/tcp.py +0 -0
  38. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/fieldtypes/net/udp.py +0 -0
  39. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/jsonpacker.py +0 -0
  40. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/packer.py +0 -0
  41. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/selector.py +0 -0
  42. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/stream.py +0 -0
  43. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/tools/__init__.py +0 -0
  44. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/tools/geoip.py +0 -0
  45. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/tools/rdump.py +0 -0
  46. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/utils.py +0 -0
  47. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow/record/whitelist.py +0 -0
  48. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/SOURCES.txt +0 -0
  49. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/dependency_links.txt +0 -0
  50. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/entry_points.txt +0 -0
  51. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/requires.txt +0 -0
  52. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/flow.record.egg-info/top_level.txt +0 -0
  53. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/pyproject.toml +0 -0
  54. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/setup.cfg +0 -0
  55. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/__init__.py +0 -0
  56. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/_utils.py +0 -0
  57. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/docs/Makefile +0 -0
  58. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/docs/conf.py +0 -0
  59. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/docs/index.rst +0 -0
  60. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/selector_explain_example.py +0 -0
  61. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/standalone_test.py +0 -0
  62. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_avro.py +0 -0
  63. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_avro_adapter.py +0 -0
  64. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_compiled_selector.py +0 -0
  65. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_csv_adapter.py +0 -0
  66. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_deprecations.py +0 -0
  67. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_fieldtype_ip.py +0 -0
  68. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_fieldtypes.py +0 -0
  69. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_json_packer.py +0 -0
  70. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_json_record_adapter.py +0 -0
  71. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_multi_timestamp.py +0 -0
  72. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_packer.py +0 -0
  73. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_rdump.py +0 -0
  74. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_record_adapter.py +0 -0
  75. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_record_descriptor.py +0 -0
  76. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_regression.py +0 -0
  77. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_selector.py +0 -0
  78. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_splunk_adapter.py +0 -0
  79. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/test_sqlite_duckdb_adapter.py +0 -0
  80. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tests/utils_inspect.py +0 -0
  81. {flow.record-3.15.dev7 → flow.record-3.15.dev8}/tox.ini +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flow.record
3
- Version: 3.15.dev7
3
+ Version: 3.15.dev8
4
4
  Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -2,6 +2,7 @@ import gzip
2
2
  import os
3
3
 
4
4
  from flow.record.base import (
5
+ IGNORE_FIELDS_FOR_COMPARISON,
5
6
  RECORD_VERSION,
6
7
  RECORDSTREAM_MAGIC,
7
8
  DynamicDescriptor,
@@ -20,6 +21,7 @@ from flow.record.base import (
20
21
  open_path,
21
22
  open_path_or_stream,
22
23
  open_stream,
24
+ set_ignored_fields_for_comparison,
23
25
  stream,
24
26
  )
25
27
  from flow.record.jsonpacker import JsonRecordPacker
@@ -35,6 +37,7 @@ from flow.record.stream import (
35
37
  )
36
38
 
37
39
  __all__ = [
40
+ "IGNORE_FIELDS_FOR_COMPARISON",
38
41
  "RECORD_VERSION",
39
42
  "RECORDSTREAM_MAGIC",
40
43
  "FieldType",
@@ -54,6 +57,7 @@ __all__ = [
54
57
  "open_path_or_stream",
55
58
  "open_path",
56
59
  "open_stream",
60
+ "set_ignored_fields_for_comparison",
57
61
  "stream",
58
62
  "dynamic_fieldtype",
59
63
  "DynamicDescriptor",
@@ -15,7 +15,17 @@ import warnings
15
15
  from datetime import datetime, timezone
16
16
  from itertools import zip_longest
17
17
  from pathlib import Path
18
- from typing import IO, Any, BinaryIO, Iterator, Mapping, Optional, Sequence, Union
18
+ from typing import (
19
+ IO,
20
+ Any,
21
+ BinaryIO,
22
+ Iterable,
23
+ Iterator,
24
+ Mapping,
25
+ Optional,
26
+ Sequence,
27
+ Union,
28
+ )
19
29
  from urllib.parse import parse_qsl, urlparse
20
30
 
21
31
  from flow.record.adapter import AbstractReader, AbstractWriter
@@ -96,6 +106,18 @@ class {name}(Record):
96
106
  """
97
107
 
98
108
 
109
+ if env_excluded_fields := os.environ.get("FLOW_RECORD_IGNORE"):
110
+ IGNORE_FIELDS_FOR_COMPARISON = set(env_excluded_fields.split(","))
111
+ else:
112
+ IGNORE_FIELDS_FOR_COMPARISON = set()
113
+
114
+
115
+ def set_ignored_fields_for_comparison(ignored_fields: Iterable[str]) -> None:
116
+ """Can be used to update the IGNORE_FIELDS_FOR_COMPARISON from outside the flow.record package scope"""
117
+ global IGNORE_FIELDS_FOR_COMPARISON
118
+ IGNORE_FIELDS_FOR_COMPARISON = set(ignored_fields)
119
+
120
+
99
121
  class FieldType:
100
122
  def _typename(self):
101
123
  t = type(self)
@@ -117,14 +139,20 @@ class Record:
117
139
  def __eq__(self, other):
118
140
  if not isinstance(other, Record):
119
141
  return False
120
- return self._pack() == other._pack()
121
142
 
122
- def _pack(self, unversioned=False):
143
+ return self._pack(excluded_fields=IGNORE_FIELDS_FOR_COMPARISON) == other._pack(
144
+ excluded_fields=IGNORE_FIELDS_FOR_COMPARISON
145
+ )
146
+
147
+ def _pack(self, unversioned=False, excluded_fields: list = None):
123
148
  values = []
124
149
  for k in self.__slots__:
125
150
  v = getattr(self, k)
126
151
  v = v._pack() if isinstance(v, FieldType) else v
127
152
 
153
+ if excluded_fields and k in excluded_fields:
154
+ continue
155
+
128
156
  # Skip version field if requested (only for compatibility reasons)
129
157
  if unversioned and k == "_version" and v == 1:
130
158
  continue
@@ -160,6 +188,9 @@ class Record:
160
188
  raise ValueError("Got unexpected field names: {kwds!r}".format(kwds=list(kwds)))
161
189
  return result
162
190
 
191
+ def __hash__(self) -> int:
192
+ return hash(self._pack(excluded_fields=IGNORE_FIELDS_FOR_COMPARISON))
193
+
163
194
  def __repr__(self):
164
195
  return "<{} {}>".format(
165
196
  self._desc.name, " ".join("{}={!r}".format(k, getattr(self, k)) for k in self._desc.fields)
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '3.15.dev7'
16
- __version_tuple__ = version_tuple = (3, 15, 'dev7')
15
+ __version__ = version = '3.15.dev8'
16
+ __version_tuple__ = version_tuple = (3, 15, 'dev8')
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: flow.record
3
- Version: 3.15.dev7
3
+ Version: 3.15.dev8
4
4
  Summary: A library for defining and creating structured data (called records) that can be streamed to disk or piped to other tools that use flow.record
5
5
  Author-email: Dissect Team <dissect@fox-it.com>
6
6
  License: Affero General Public License v3
@@ -1,4 +1,7 @@
1
+ import importlib
2
+ import os
1
3
  import sys
4
+ from unittest.mock import patch
2
5
 
3
6
  import pytest
4
7
 
@@ -15,7 +18,11 @@ from flow.record import (
15
18
  fieldtypes,
16
19
  record_stream,
17
20
  )
18
- from flow.record.base import merge_record_descriptors, normalize_fieldname
21
+ from flow.record.base import (
22
+ merge_record_descriptors,
23
+ normalize_fieldname,
24
+ set_ignored_fields_for_comparison,
25
+ )
19
26
  from flow.record.exceptions import RecordDescriptorError
20
27
  from flow.record.stream import RecordFieldRewriter
21
28
 
@@ -792,3 +799,60 @@ def test_normalize_fieldname():
792
799
  assert normalize_fieldname("my name (with) parentheses") == "my_name__with__parentheses"
793
800
  assert normalize_fieldname("_generated") == "_generated"
794
801
  assert normalize_fieldname("_source") == "_source"
802
+
803
+
804
+ def test_compare_global_variable():
805
+ TestRecord = RecordDescriptor(
806
+ "test/record",
807
+ [
808
+ ("string", "firstname"),
809
+ ("string", "lastname"),
810
+ ],
811
+ )
812
+
813
+ same_same = TestRecord(firstname="James", lastname="Bond")
814
+ but_different = TestRecord(firstname="Ethan", lastname="Hunt")
815
+ but_still_same = TestRecord(firstname="Andrew", lastname="Bond")
816
+
817
+ records = [same_same, but_different, but_still_same]
818
+
819
+ assert same_same != but_still_same
820
+
821
+ set_ignored_fields_for_comparison({"_generated", "firstname"})
822
+ assert same_same == but_still_same
823
+ assert same_same != but_different
824
+ assert len(set(records)) == 2
825
+
826
+
827
+ def test_compare_environment_variable():
828
+ with patch.dict(os.environ), patch.dict(sys.modules):
829
+ os.environ["FLOW_RECORD_IGNORE"] = "_generated,lastname"
830
+
831
+ # Force a re-import of flow.record so the global variable gets re-initialized based on the environment variable
832
+ keys = [key for key in sys.modules if key == "flow" or "flow." in key]
833
+ for key in keys:
834
+ del sys.modules[key]
835
+
836
+ importlib.import_module("flow.record")
837
+
838
+ from flow.record import IGNORE_FIELDS_FOR_COMPARISON, RecordDescriptor
839
+
840
+ assert IGNORE_FIELDS_FOR_COMPARISON == {"_generated", "lastname"}
841
+
842
+ TestRecord = RecordDescriptor(
843
+ "test/record",
844
+ [
845
+ ("string", "firstname"),
846
+ ("string", "lastname"),
847
+ ],
848
+ )
849
+
850
+ same_same = TestRecord(firstname="John", lastname="Rambo")
851
+ but_different = TestRecord(firstname="Johnny", lastname="English")
852
+ but_still_same = TestRecord(firstname="John", lastname="McClane")
853
+
854
+ records = [same_same, but_different, but_still_same]
855
+
856
+ assert same_same == but_still_same
857
+ assert same_same != but_different
858
+ assert len(set(records)) == 2
File without changes
File without changes