pgsync 3.3.0__tar.gz → 4.0.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.
Files changed (76) hide show
  1. {pgsync-3.3.0 → pgsync-4.0.0}/PKG-INFO +34 -20
  2. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/__init__.py +1 -1
  3. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/base.py +4 -4
  4. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/querybuilder.py +10 -12
  5. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/sync.py +4 -5
  6. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync.egg-info/PKG-INFO +34 -20
  7. pgsync-4.0.0/pgsync.egg-info/requires.txt +30 -0
  8. pgsync-3.3.0/pgsync.egg-info/requires.txt +0 -30
  9. {pgsync-3.3.0 → pgsync-4.0.0}/AUTHORS.rst +0 -0
  10. {pgsync-3.3.0 → pgsync-4.0.0}/CONTRIBUTING.rst +0 -0
  11. {pgsync-3.3.0 → pgsync-4.0.0}/HISTORY.rst +0 -0
  12. {pgsync-3.3.0 → pgsync-4.0.0}/LICENSE +0 -0
  13. {pgsync-3.3.0 → pgsync-4.0.0}/MANIFEST.in +0 -0
  14. {pgsync-3.3.0 → pgsync-4.0.0}/README.md +0 -0
  15. {pgsync-3.3.0 → pgsync-4.0.0}/README.rst +0 -0
  16. {pgsync-3.3.0 → pgsync-4.0.0}/bin/bootstrap +0 -0
  17. {pgsync-3.3.0 → pgsync-4.0.0}/bin/parallel_sync +0 -0
  18. {pgsync-3.3.0 → pgsync-4.0.0}/bin/pgsync +0 -0
  19. {pgsync-3.3.0 → pgsync-4.0.0}/docs/Makefile +0 -0
  20. {pgsync-3.3.0 → pgsync-4.0.0}/docs/authors.rst +0 -0
  21. {pgsync-3.3.0 → pgsync-4.0.0}/docs/changelog.rst +0 -0
  22. {pgsync-3.3.0 → pgsync-4.0.0}/docs/conf.py +0 -0
  23. {pgsync-3.3.0 → pgsync-4.0.0}/docs/contributing.rst +0 -0
  24. {pgsync-3.3.0 → pgsync-4.0.0}/docs/history.rst +0 -0
  25. {pgsync-3.3.0 → pgsync-4.0.0}/docs/index.rst +0 -0
  26. {pgsync-3.3.0 → pgsync-4.0.0}/docs/installation.rst +0 -0
  27. {pgsync-3.3.0 → pgsync-4.0.0}/docs/logo.png +0 -0
  28. {pgsync-3.3.0 → pgsync-4.0.0}/docs/make.bat +0 -0
  29. {pgsync-3.3.0 → pgsync-4.0.0}/docs/readme.rst +0 -0
  30. {pgsync-3.3.0 → pgsync-4.0.0}/docs/usage.rst +0 -0
  31. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/constants.py +0 -0
  32. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/exc.py +0 -0
  33. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/helper.py +0 -0
  34. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/node.py +0 -0
  35. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/plugin.py +0 -0
  36. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/redisqueue.py +0 -0
  37. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/search_client.py +0 -0
  38. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/settings.py +0 -0
  39. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/singleton.py +0 -0
  40. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/transform.py +0 -0
  41. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/trigger.py +0 -0
  42. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/urls.py +0 -0
  43. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/utils.py +0 -0
  44. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync/view.py +0 -0
  45. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync.egg-info/SOURCES.txt +0 -0
  46. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync.egg-info/dependency_links.txt +0 -0
  47. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync.egg-info/not-zip-safe +0 -0
  48. {pgsync-3.3.0 → pgsync-4.0.0}/pgsync.egg-info/top_level.txt +0 -0
  49. {pgsync-3.3.0 → pgsync-4.0.0}/pyproject.toml +0 -0
  50. {pgsync-3.3.0 → pgsync-4.0.0}/setup.cfg +0 -0
  51. {pgsync-3.3.0 → pgsync-4.0.0}/setup.py +0 -0
  52. {pgsync-3.3.0 → pgsync-4.0.0}/tests/__init__.py +0 -0
  53. {pgsync-3.3.0 → pgsync-4.0.0}/tests/conftest.py +0 -0
  54. {pgsync-3.3.0 → pgsync-4.0.0}/tests/fixtures/schema.json +0 -0
  55. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_base.py +0 -0
  56. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_constants.py +0 -0
  57. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_env_vars.py +0 -0
  58. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_helper.py +0 -0
  59. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_log_handlers.py +0 -0
  60. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_node.py +0 -0
  61. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_query_builder.py +0 -0
  62. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_redisqueue.py +0 -0
  63. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_search_client.py +0 -0
  64. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_settings.py +0 -0
  65. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_sync.py +0 -0
  66. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_sync_nested_children.py +0 -0
  67. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_sync_root.py +0 -0
  68. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_sync_single_child_fk_on_child.py +0 -0
  69. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_sync_single_child_fk_on_parent.py +0 -0
  70. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_transform.py +0 -0
  71. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_trigger.py +0 -0
  72. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_unique_behaviour.py +0 -0
  73. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_urls.py +0 -0
  74. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_utils.py +0 -0
  75. {pgsync-3.3.0 → pgsync-4.0.0}/tests/test_view.py +0 -0
  76. {pgsync-3.3.0 → pgsync-4.0.0}/tests/testing_utils.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pgsync
3
- Version: 3.3.0
3
+ Version: 4.0.0
4
4
  Summary: Postgres to Elasticsearch/OpenSearch sync
5
5
  Home-page: https://github.com/toluaina/pgsync
6
6
  Author: Tolu Aina
@@ -30,36 +30,50 @@ Requires-Python: >=3.9.0
30
30
  Description-Content-Type: text/markdown
31
31
  License-File: LICENSE
32
32
  License-File: AUTHORS.rst
33
- Requires-Dist: async-timeout==5.0.0
34
- Requires-Dist: boto3==1.35.54
35
- Requires-Dist: botocore==1.35.54
36
- Requires-Dist: certifi==2024.8.30
37
- Requires-Dist: charset-normalizer==3.4.0
38
- Requires-Dist: click==8.1.7
39
- Requires-Dist: elastic-transport==8.15.1
40
- Requires-Dist: elasticsearch==8.15.1
41
- Requires-Dist: elasticsearch-dsl==8.15.4
42
- Requires-Dist: environs==11.0.0
33
+ Requires-Dist: async-timeout==5.0.1
34
+ Requires-Dist: boto3==1.37.11
35
+ Requires-Dist: botocore==1.37.11
36
+ Requires-Dist: certifi==2025.1.31
37
+ Requires-Dist: charset-normalizer==3.4.1
38
+ Requires-Dist: click==8.1.8
39
+ Requires-Dist: elastic-transport==8.17.0
40
+ Requires-Dist: elasticsearch==8.17.2
41
+ Requires-Dist: elasticsearch-dsl==8.17.1
42
+ Requires-Dist: environs==14.1.1
43
43
  Requires-Dist: events==0.5
44
44
  Requires-Dist: greenlet==3.1.1
45
45
  Requires-Dist: idna==3.10
46
46
  Requires-Dist: jmespath==1.0.1
47
- Requires-Dist: marshmallow==3.23.1
47
+ Requires-Dist: marshmallow==3.26.1
48
48
  Requires-Dist: opensearch-dsl==2.1.0
49
- Requires-Dist: opensearch-py==2.7.1
50
- Requires-Dist: packaging==24.1
49
+ Requires-Dist: opensearch-py==2.8.0
50
+ Requires-Dist: packaging==24.2
51
51
  Requires-Dist: psycopg2-binary==2.9.10
52
52
  Requires-Dist: python-dateutil==2.9.0.post0
53
53
  Requires-Dist: python-dotenv==1.0.1
54
- Requires-Dist: redis==5.2.0
54
+ Requires-Dist: redis==5.2.1
55
55
  Requires-Dist: requests==2.32.3
56
56
  Requires-Dist: requests-aws4auth==1.3.1
57
- Requires-Dist: s3transfer==0.10.3
58
- Requires-Dist: six==1.16.0
59
- Requires-Dist: sqlalchemy==2.0.36
60
- Requires-Dist: sqlparse==0.5.1
57
+ Requires-Dist: s3transfer==0.11.4
58
+ Requires-Dist: six==1.17.0
59
+ Requires-Dist: sqlalchemy==2.0.39
60
+ Requires-Dist: sqlparse==0.5.3
61
61
  Requires-Dist: typing-extensions==4.12.2
62
62
  Requires-Dist: urllib3==1.26.20
63
+ Dynamic: author
64
+ Dynamic: author-email
65
+ Dynamic: classifier
66
+ Dynamic: description
67
+ Dynamic: description-content-type
68
+ Dynamic: home-page
69
+ Dynamic: keywords
70
+ Dynamic: license
71
+ Dynamic: maintainer
72
+ Dynamic: maintainer-email
73
+ Dynamic: project-url
74
+ Dynamic: requires-dist
75
+ Dynamic: requires-python
76
+ Dynamic: summary
63
77
 
64
78
  # PostgreSQL to Elasticsearch/OpenSearch sync
65
79
 
@@ -2,4 +2,4 @@
2
2
 
3
3
  __author__ = "Tolu Aina"
4
4
  __email__ = "tolu@pgsync.com"
5
- __version__ = "3.3.0"
5
+ __version__ = "4.0.0"
@@ -670,7 +670,7 @@ class Base(object):
670
670
  ]:
671
671
  self.drop_triggers(schema, [table])
672
672
  queries.append(
673
- f'CREATE TRIGGER "{table}_{name}" '
673
+ f'CREATE TRIGGER "{schema}_{table}_{name}" '
674
674
  f'AFTER {" OR ".join(tg_op)} ON "{schema}"."{table}" '
675
675
  f"FOR EACH {level} EXECUTE PROCEDURE "
676
676
  f"{schema}.{TRIGGER_FUNC}()",
@@ -696,7 +696,7 @@ class Base(object):
696
696
  logger.debug(f"Dropping trigger on table: {schema}.{table}")
697
697
  for name in ("notify", "truncate"):
698
698
  queries.append(
699
- f'DROP TRIGGER IF EXISTS "{table}_{name}" ON '
699
+ f'DROP TRIGGER IF EXISTS "{schema}_{table}_{name}" ON '
700
700
  f'"{schema}"."{table}"'
701
701
  )
702
702
  if join_queries:
@@ -732,7 +732,7 @@ class Base(object):
732
732
  self.execute(
733
733
  sa.text(
734
734
  f'ALTER TABLE "{schema}"."{table}" '
735
- f"DISABLE TRIGGER {table}_{name}"
735
+ f"DISABLE TRIGGER {schema}_{table}_{name}"
736
736
  )
737
737
  )
738
738
 
@@ -749,7 +749,7 @@ class Base(object):
749
749
  self.execute(
750
750
  sa.text(
751
751
  f'ALTER TABLE "{schema}"."{table}" '
752
- f"ENABLE TRIGGER {table}_{name}"
752
+ f"ENABLE TRIGGER {schema}_{table}_{name}"
753
753
  )
754
754
  )
755
755
 
@@ -346,6 +346,7 @@ class QueryBuilder(threading.local):
346
346
  < txmax
347
347
  )
348
348
 
349
+ # NB: only apply filters to the root node
349
350
  if node._filters:
350
351
  node._subquery = node._subquery.where(sa.and_(*node._filters))
351
352
  node._subquery = node._subquery.alias()
@@ -419,7 +420,7 @@ class QueryBuilder(threading.local):
419
420
  self.from_obj = child.parent.model
420
421
 
421
422
  if child._filters:
422
- self.isouter = False
423
+
423
424
  for _filter in child._filters:
424
425
  if isinstance(_filter, sa.sql.elements.BinaryExpression):
425
426
  for column in _filter._orig:
@@ -569,7 +570,6 @@ class QueryBuilder(threading.local):
569
570
  from_obj = node.model
570
571
 
571
572
  if child._filters:
572
- self.isouter = False
573
573
 
574
574
  for _filter in child._filters:
575
575
  if isinstance(_filter, sa.sql.elements.BinaryExpression):
@@ -689,9 +689,6 @@ class QueryBuilder(threading.local):
689
689
  == through.model.c[right_foreign_keys[i]]
690
690
  )
691
691
 
692
- if node._filters:
693
- self.isouter = False
694
-
695
692
  op = sa.and_
696
693
  if node.table == node.parent.table:
697
694
  op = sa.or_
@@ -703,8 +700,10 @@ class QueryBuilder(threading.local):
703
700
  )
704
701
 
705
702
  node._subquery = inner_subquery.select_from(from_obj)
706
- if node._filters:
707
- node._subquery = node._subquery.where(sa.and_(*node._filters))
703
+
704
+ # NB do not apply filters to the child node as they are applied to the parent
705
+ # if node._filters:
706
+ # node._subquery = node._subquery.where(sa.and_(*node._filters))
708
707
 
709
708
  node._subquery = node._subquery.group_by(
710
709
  *[through.model.c[column] for column in foreign_keys[through.name]]
@@ -746,7 +745,6 @@ class QueryBuilder(threading.local):
746
745
  from_obj = node.model
747
746
 
748
747
  if child._filters:
749
- self.isouter = False
750
748
 
751
749
  for _filter in child._filters:
752
750
  if isinstance(_filter, sa.sql.elements.BinaryExpression):
@@ -776,11 +774,10 @@ class QueryBuilder(threading.local):
776
774
  == column.value
777
775
  )
778
776
 
779
- isouter = not (len(child._filters) > 0)
780
777
  from_obj = from_obj.join(
781
778
  child._subquery,
782
779
  onclause=sa.and_(*onclause),
783
- isouter=isouter,
780
+ isouter=self.isouter,
784
781
  )
785
782
 
786
783
  foreign_keys: dict = self.get_foreign_keys(node.parent, node)
@@ -865,8 +862,9 @@ class QueryBuilder(threading.local):
865
862
  )
866
863
  node._subquery = node._subquery.where(sa.and_(*where))
867
864
 
868
- if node._filters:
869
- node._subquery = node._subquery.where(sa.and_(*node._filters))
865
+ # NB do not apply filters to the child node as they are applied to the parent
866
+ # if node._filters:
867
+ # node._subquery = node._subquery.where(sa.and_(*node._filters))
870
868
 
871
869
  if node.relationship.type == ONE_TO_MANY:
872
870
  node._subquery = node._subquery.group_by(
@@ -577,11 +577,10 @@ class Sync(Base, metaclass=Singleton):
577
577
  node, payload, foreign_keys, _filters
578
578
  )
579
579
 
580
- # through table with a direct references to root
581
- if not _filters:
582
- _filters = self._through_node_resolver(
583
- node, payload, _filters
584
- )
580
+ # also check through table with a direct references to root
581
+ _filters = self._through_node_resolver(
582
+ node, payload, _filters
583
+ )
585
584
 
586
585
  if _filters:
587
586
  filters[self.tree.root.table].extend(_filters)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.2
2
2
  Name: pgsync
3
- Version: 3.3.0
3
+ Version: 4.0.0
4
4
  Summary: Postgres to Elasticsearch/OpenSearch sync
5
5
  Home-page: https://github.com/toluaina/pgsync
6
6
  Author: Tolu Aina
@@ -30,36 +30,50 @@ Requires-Python: >=3.9.0
30
30
  Description-Content-Type: text/markdown
31
31
  License-File: LICENSE
32
32
  License-File: AUTHORS.rst
33
- Requires-Dist: async-timeout==5.0.0
34
- Requires-Dist: boto3==1.35.54
35
- Requires-Dist: botocore==1.35.54
36
- Requires-Dist: certifi==2024.8.30
37
- Requires-Dist: charset-normalizer==3.4.0
38
- Requires-Dist: click==8.1.7
39
- Requires-Dist: elastic-transport==8.15.1
40
- Requires-Dist: elasticsearch==8.15.1
41
- Requires-Dist: elasticsearch-dsl==8.15.4
42
- Requires-Dist: environs==11.0.0
33
+ Requires-Dist: async-timeout==5.0.1
34
+ Requires-Dist: boto3==1.37.11
35
+ Requires-Dist: botocore==1.37.11
36
+ Requires-Dist: certifi==2025.1.31
37
+ Requires-Dist: charset-normalizer==3.4.1
38
+ Requires-Dist: click==8.1.8
39
+ Requires-Dist: elastic-transport==8.17.0
40
+ Requires-Dist: elasticsearch==8.17.2
41
+ Requires-Dist: elasticsearch-dsl==8.17.1
42
+ Requires-Dist: environs==14.1.1
43
43
  Requires-Dist: events==0.5
44
44
  Requires-Dist: greenlet==3.1.1
45
45
  Requires-Dist: idna==3.10
46
46
  Requires-Dist: jmespath==1.0.1
47
- Requires-Dist: marshmallow==3.23.1
47
+ Requires-Dist: marshmallow==3.26.1
48
48
  Requires-Dist: opensearch-dsl==2.1.0
49
- Requires-Dist: opensearch-py==2.7.1
50
- Requires-Dist: packaging==24.1
49
+ Requires-Dist: opensearch-py==2.8.0
50
+ Requires-Dist: packaging==24.2
51
51
  Requires-Dist: psycopg2-binary==2.9.10
52
52
  Requires-Dist: python-dateutil==2.9.0.post0
53
53
  Requires-Dist: python-dotenv==1.0.1
54
- Requires-Dist: redis==5.2.0
54
+ Requires-Dist: redis==5.2.1
55
55
  Requires-Dist: requests==2.32.3
56
56
  Requires-Dist: requests-aws4auth==1.3.1
57
- Requires-Dist: s3transfer==0.10.3
58
- Requires-Dist: six==1.16.0
59
- Requires-Dist: sqlalchemy==2.0.36
60
- Requires-Dist: sqlparse==0.5.1
57
+ Requires-Dist: s3transfer==0.11.4
58
+ Requires-Dist: six==1.17.0
59
+ Requires-Dist: sqlalchemy==2.0.39
60
+ Requires-Dist: sqlparse==0.5.3
61
61
  Requires-Dist: typing-extensions==4.12.2
62
62
  Requires-Dist: urllib3==1.26.20
63
+ Dynamic: author
64
+ Dynamic: author-email
65
+ Dynamic: classifier
66
+ Dynamic: description
67
+ Dynamic: description-content-type
68
+ Dynamic: home-page
69
+ Dynamic: keywords
70
+ Dynamic: license
71
+ Dynamic: maintainer
72
+ Dynamic: maintainer-email
73
+ Dynamic: project-url
74
+ Dynamic: requires-dist
75
+ Dynamic: requires-python
76
+ Dynamic: summary
63
77
 
64
78
  # PostgreSQL to Elasticsearch/OpenSearch sync
65
79
 
@@ -0,0 +1,30 @@
1
+ async-timeout==5.0.1
2
+ boto3==1.37.11
3
+ botocore==1.37.11
4
+ certifi==2025.1.31
5
+ charset-normalizer==3.4.1
6
+ click==8.1.8
7
+ elastic-transport==8.17.0
8
+ elasticsearch==8.17.2
9
+ elasticsearch-dsl==8.17.1
10
+ environs==14.1.1
11
+ events==0.5
12
+ greenlet==3.1.1
13
+ idna==3.10
14
+ jmespath==1.0.1
15
+ marshmallow==3.26.1
16
+ opensearch-dsl==2.1.0
17
+ opensearch-py==2.8.0
18
+ packaging==24.2
19
+ psycopg2-binary==2.9.10
20
+ python-dateutil==2.9.0.post0
21
+ python-dotenv==1.0.1
22
+ redis==5.2.1
23
+ requests==2.32.3
24
+ requests-aws4auth==1.3.1
25
+ s3transfer==0.11.4
26
+ six==1.17.0
27
+ sqlalchemy==2.0.39
28
+ sqlparse==0.5.3
29
+ typing-extensions==4.12.2
30
+ urllib3==1.26.20
@@ -1,30 +0,0 @@
1
- async-timeout==5.0.0
2
- boto3==1.35.54
3
- botocore==1.35.54
4
- certifi==2024.8.30
5
- charset-normalizer==3.4.0
6
- click==8.1.7
7
- elastic-transport==8.15.1
8
- elasticsearch==8.15.1
9
- elasticsearch-dsl==8.15.4
10
- environs==11.0.0
11
- events==0.5
12
- greenlet==3.1.1
13
- idna==3.10
14
- jmespath==1.0.1
15
- marshmallow==3.23.1
16
- opensearch-dsl==2.1.0
17
- opensearch-py==2.7.1
18
- packaging==24.1
19
- psycopg2-binary==2.9.10
20
- python-dateutil==2.9.0.post0
21
- python-dotenv==1.0.1
22
- redis==5.2.0
23
- requests==2.32.3
24
- requests-aws4auth==1.3.1
25
- s3transfer==0.10.3
26
- six==1.16.0
27
- sqlalchemy==2.0.36
28
- sqlparse==0.5.1
29
- typing-extensions==4.12.2
30
- urllib3==1.26.20
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
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