ansys-pre-commit-hooks 0.7.0__tar.gz → 0.7.2__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 (33) hide show
  1. {ansys_pre_commit_hooks-0.7.0/src/ansys_pre_commit_hooks.egg-info → ansys_pre_commit_hooks-0.7.2}/PKG-INFO +2 -2
  2. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/pyproject.toml +20 -14
  3. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/setup.py +1 -1
  4. ansys_pre_commit_hooks-0.7.2/src/ansys/pre_commit_hooks/VERSION +1 -0
  5. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/add_license_headers.py +41 -20
  6. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2/src/ansys_pre_commit_hooks.egg-info}/PKG-INFO +2 -2
  7. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys_pre_commit_hooks.egg-info/requires.txt +1 -1
  8. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/tests/test_add_license_headers.py +171 -0
  9. ansys_pre_commit_hooks-0.7.0/src/ansys/pre_commit_hooks/VERSION +0 -1
  10. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/LICENSE +0 -0
  11. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/MANIFEST.in +0 -0
  12. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/README.rst +0 -0
  13. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/setup.cfg +0 -0
  14. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/__init__.py +0 -0
  15. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/assets/.reuse/templates/ansys.jinja2 +0 -0
  16. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/assets/LICENSES/Apache-2.0.txt +0 -0
  17. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/assets/LICENSES/MIT.txt +0 -0
  18. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/assets/licenses.json +0 -0
  19. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/tech_review.py +0 -0
  20. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/AUTHORS +0 -0
  21. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/CODE_OF_CONDUCT.md +0 -0
  22. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/CONTRIBUTING.md +0 -0
  23. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/CONTRIBUTORS.md +0 -0
  24. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/LICENSE +0 -0
  25. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/README.md +0 -0
  26. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/README.rst +0 -0
  27. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys/pre_commit_hooks/templates/dependabot.yml +0 -0
  28. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys_pre_commit_hooks.egg-info/SOURCES.txt +0 -0
  29. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys_pre_commit_hooks.egg-info/dependency_links.txt +0 -0
  30. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys_pre_commit_hooks.egg-info/entry_points.txt +0 -0
  31. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/src/ansys_pre_commit_hooks.egg-info/top_level.txt +0 -0
  32. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/tests/test_metadata.py +0 -0
  33. {ansys_pre_commit_hooks-0.7.0 → ansys_pre_commit_hooks-0.7.2}/tests/test_tech_review.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansys-pre-commit-hooks
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Home-page: https://github.com/ansys/pre-commit-hooks
5
5
  Author: ANSYS, Inc.
6
6
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
@@ -24,7 +24,7 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Requires-Python: >=3.10,<4
25
25
  Description-Content-Type: text/x-rst
26
26
  License-File: LICENSE
27
- Requires-Dist: GitPython==3.1.46
27
+ Requires-Dist: GitPython==3.1.49
28
28
  Requires-Dist: importlib-metadata==9.0.0
29
29
  Requires-Dist: Jinja2==3.1.6
30
30
  Requires-Dist: reuse==6.2.0
@@ -1,7 +1,7 @@
1
1
  [build-system]
2
2
  requires = [
3
3
  "setuptools>=65.5.1",
4
- "GitPython==3.1.46",
4
+ "GitPython==3.1.49",
5
5
  "Jinja2==3.1.6",
6
6
  "reuse==6.2.0",
7
7
  "requests==2.33.1",
@@ -20,7 +20,7 @@ license-files = ["LICENSE"]
20
20
  authors = [{ name = "ANSYS, Inc.", email = "pyansys.core@ansys.com" }]
21
21
  maintainers = [{ name = "ANSYS, Inc.", email = "pyansys.core@ansys.com" }]
22
22
  dependencies = [
23
- "GitPython==3.1.46",
23
+ "GitPython==3.1.49",
24
24
  "importlib-metadata==9.0.0",
25
25
  "Jinja2==3.1.6",
26
26
  "reuse==6.2.0",
@@ -105,13 +105,13 @@ title_format = "`{version} <https://github.com/ansys/pre-commit-hooks/releases/t
105
105
  issue_format = "`#{issue} <https://github.com/ansys/pre-commit-hooks/pull/{issue}>`_"
106
106
 
107
107
  [[tool.towncrier.type]]
108
- directory = "added"
109
- name = "Added"
108
+ directory = "breaking"
109
+ name = "Breaking"
110
110
  showcontent = true
111
111
 
112
112
  [[tool.towncrier.type]]
113
- directory = "changed"
114
- name = "Changed"
113
+ directory = "added"
114
+ name = "Added"
115
115
  showcontent = true
116
116
 
117
117
  [[tool.towncrier.type]]
@@ -120,26 +120,32 @@ name = "Fixed"
120
120
  showcontent = true
121
121
 
122
122
  [[tool.towncrier.type]]
123
- directory = "dependencies"
124
- name = "Dependencies"
123
+ directory = "documentation"
124
+ name = "Documentation"
125
125
  showcontent = true
126
126
 
127
127
  [[tool.towncrier.type]]
128
- directory = "miscellaneous"
129
- name = "Miscellaneous"
128
+ directory = "dependencies"
129
+ name = "Dependencies"
130
130
  showcontent = true
131
131
 
132
132
  [[tool.towncrier.type]]
133
- directory = "documentation"
134
- name = "Documentation"
133
+ directory = "maintenance"
134
+ name = "Maintenance"
135
135
  showcontent = true
136
136
 
137
137
  [[tool.towncrier.type]]
138
- directory = "maintenance"
139
- name = "Maintenance"
138
+ directory = "miscellaneous"
139
+ name = "Miscellaneous"
140
140
  showcontent = true
141
141
 
142
142
  [[tool.towncrier.type]]
143
143
  directory = "test"
144
144
  name = "Test"
145
145
  showcontent = true
146
+
147
+ [[tool.towncrier.type]]
148
+ directory = "changed"
149
+ name = "Changed"
150
+ showcontent = true
151
+
@@ -39,7 +39,7 @@ setup(
39
39
  url="https://github.com/ansys/pre-commit-hooks",
40
40
  python_requires=">=3.10,<4",
41
41
  install_requires=[
42
- "GitPython==3.1.46",
42
+ "GitPython==3.1.49",
43
43
  "importlib-metadata==9.0.0",
44
44
  "Jinja2==3.1.6",
45
45
  "reuse==6.2.0",
@@ -29,6 +29,7 @@ A license header consists of the Ansys copyright statement and licensing informa
29
29
  import argparse
30
30
  from datetime import date as dt
31
31
  import filecmp
32
+ import io
32
33
  from pathlib import Path
33
34
  import re
34
35
  import shutil
@@ -201,22 +202,31 @@ def update_license_file(
201
202
  template_parent_dir = hook_loc / "assets" / "LICENSES"
202
203
  generate_license_file(template_parent_dir, year_span, repo_license_path, license)
203
204
  else:
204
- # The year regex to match the year or year range in the LICENSE file
205
- year_regex = r"(\d{4}) - (\d{4})|\d{4}"
206
-
207
205
  content = existing_content
208
- # Get the first instance of the year range, either one year or a range of years
209
- year_range_match = re.search(year_regex, content)
210
- # Get the group from the year_range_match
211
- year_range = year_range_match.group()
212
-
213
- # Replace the current year span with the updated year span
214
- if year_range != year_span:
215
- # Update the year span in the LICENSE file. "1" is the max number of replacements
216
- content = re.sub(year_regex, year_span, content, 1)
217
-
218
- with repo_license_path.open(encoding="utf-8", newline="", mode="w") as file:
219
- file.write(content)
206
+ # Match the year (or year range) specifically in the Copyright line.
207
+ # Searching for the first year in the file is incorrect for licenses like
208
+ # Apache-2.0 whose boilerplate contains "January 2004" before the copyright
209
+ # notice, which would otherwise be overwritten with the current year.
210
+ copyright_year_regex = r"(Copyright(?:\s+\(c\))?\s+)((?:\d{4}\s+-\s+)?\d{4})"
211
+ year_range_match = re.search(copyright_year_regex, content)
212
+
213
+ if year_range_match:
214
+ # Get the group from the year_range_match
215
+ year_range = year_range_match.group(2)
216
+
217
+ # Replace the current year span with the updated year span
218
+ if year_range != year_span:
219
+ # Update the year span in the LICENSE file. "1" is the max number of replacements
220
+ content = re.sub(copyright_year_regex, rf"\g<1>{year_span}", content, 1)
221
+
222
+ with repo_license_path.open(encoding="utf-8", newline="", mode="w") as file:
223
+ file.write(content)
224
+ else:
225
+ # No year found in the Copyright line (e.g. "Copyright [yyyy]" placeholder
226
+ # in a stock Apache-2.0 LICENSE). Regenerate the file from the template.
227
+ hook_loc = Path(__file__).parent.resolve()
228
+ template_parent_dir = hook_loc / "assets" / "LICENSES"
229
+ generate_license_file(template_parent_dir, year_span, repo_license_path, license)
220
230
 
221
231
  # If the file changed, print a message that the LICENSE file was changed
222
232
  if not check_same_content(temp_file, repo_license_path):
@@ -340,7 +350,13 @@ def mkdirs_and_link(
340
350
  dest.unlink()
341
351
 
342
352
  # Make symbolic links to files within the assets folder
343
- dest.symlink_to(src)
353
+ # Fall back to copying if symlinks are not permitted (e.g. Windows without Developer Mode)
354
+ try:
355
+ dest.symlink_to(src)
356
+ except OSError:
357
+ import shutil
358
+
359
+ shutil.copy2(src, dest)
344
360
 
345
361
 
346
362
  def _has_current_header(file_path: str, copyright: list, years: str) -> bool:
@@ -501,7 +517,7 @@ def non_recursive_file_check(
501
517
  before_hook = NamedTemporaryFile(mode="w", delete=False).name
502
518
  shutil.copyfile(file, before_hook)
503
519
  _strip_reuse_header(file)
504
- add_header(copyright, license, years, file, template, commented, sys.stdout)
520
+ add_header(copyright, license, years, file, template, commented, io.StringIO())
505
521
  if not check_same_content(before_hook, file):
506
522
  changed_headers = 1
507
523
  print(f"Successfully changed header of {file}")
@@ -667,7 +683,7 @@ def add_header(
667
683
  The template to use for the license header. For example, "ansys.jinja2".
668
684
  commented: bool
669
685
  Whether the template is commented or not.
670
- tmp: Union[NamedTemporaryFile, IO[str]]
686
+ out: Union[NamedTemporaryFile, IO[str]]
671
687
  Temporary file to capture the stdout of the add_header_to_file() function or ``sys.stdout``.
672
688
  """
673
689
  from reuse.cli.annotate import add_header_to_file, get_comment_style, get_reuse_info
@@ -686,12 +702,17 @@ def add_header(
686
702
  )
687
703
 
688
704
  # Add or update the header in the file with the REUSE information.
705
+ comment_style = get_comment_style(file)
706
+ if comment_style is None:
707
+ print(f"Skipping {file}: no comment style found for this file type.", file=out)
708
+ return
709
+
689
710
  add_header_to_file(
690
711
  path=file,
691
712
  reuse_info=reuse_info,
692
713
  template=template,
693
714
  template_is_commented=commented,
694
- style=f"{get_comment_style(file).SHORTHAND}",
715
+ style=f"{comment_style.SHORTHAND}",
695
716
  merge_copyrights=True,
696
717
  out=out,
697
718
  )
@@ -946,7 +967,7 @@ def main():
946
967
  "copyright": args.custom_copyright,
947
968
  "template": args.custom_template,
948
969
  "license": args.custom_license,
949
- "start_year": args.start_year,
970
+ "start_year": int(args.start_year),
950
971
  "current_year": dt.today().year,
951
972
  "git_repo": git_repo,
952
973
  }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ansys-pre-commit-hooks
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Home-page: https://github.com/ansys/pre-commit-hooks
5
5
  Author: ANSYS, Inc.
6
6
  Author-email: "ANSYS, Inc." <pyansys.core@ansys.com>
@@ -24,7 +24,7 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Requires-Python: >=3.10,<4
25
25
  Description-Content-Type: text/x-rst
26
26
  License-File: LICENSE
27
- Requires-Dist: GitPython==3.1.46
27
+ Requires-Dist: GitPython==3.1.49
28
28
  Requires-Dist: importlib-metadata==9.0.0
29
29
  Requires-Dist: Jinja2==3.1.6
30
30
  Requires-Dist: reuse==6.2.0
@@ -1,4 +1,4 @@
1
- GitPython==3.1.46
1
+ GitPython==3.1.49
2
2
  importlib-metadata==9.0.0
3
3
  Jinja2==3.1.6
4
4
  reuse==6.2.0
@@ -22,6 +22,7 @@
22
22
 
23
23
  import argparse
24
24
  from datetime import date as dt
25
+ import io
25
26
  import os
26
27
  from pathlib import Path
27
28
  import shutil
@@ -496,6 +497,108 @@ def test_copy_assets(tmp_path: pytest.TempPathFactory):
496
497
  check_ansys_header(tmp_file)
497
498
 
498
499
 
500
+ @pytest.mark.add_license_headers
501
+ def test_mkdirs_and_link_creates_symlink(tmp_path):
502
+ """Test that mkdirs_and_link creates a symlink when permissions allow."""
503
+ src_dir = tmp_path / "hook_assets"
504
+ src_dir.mkdir()
505
+ src_file = src_dir / "ansys.jinja2"
506
+ src_file.write_text("template content")
507
+
508
+ dest_dir = tmp_path / "repo" / ".reuse" / "templates"
509
+ asset_dir = str(dest_dir)
510
+
511
+ hook.mkdirs_and_link(asset_dir, str(src_dir), str(dest_dir), "ansys.jinja2")
512
+
513
+ dest_file = dest_dir / "ansys.jinja2"
514
+ assert dest_file.exists()
515
+ assert dest_file.read_text() == "template content"
516
+
517
+
518
+ @pytest.mark.add_license_headers
519
+ def test_mkdirs_and_link_fallback_copy_on_oserror(tmp_path, monkeypatch):
520
+ """Test that mkdirs_and_link falls back to copying when symlink raises OSError."""
521
+ src_dir = tmp_path / "hook_assets"
522
+ src_dir.mkdir()
523
+ src_file = src_dir / "ansys.jinja2"
524
+ src_file.write_text("template content")
525
+
526
+ dest_dir = tmp_path / "repo" / ".reuse" / "templates"
527
+ asset_dir = str(dest_dir)
528
+
529
+ # Simulate Windows symlink privilege error
530
+ monkeypatch.setattr(
531
+ Path,
532
+ "symlink_to",
533
+ lambda *_args, **_kwargs: (_ for _ in ()).throw(
534
+ OSError(1314, "A required privilege is not held by the client")
535
+ ),
536
+ )
537
+
538
+ hook.mkdirs_and_link(asset_dir, str(src_dir), str(dest_dir), "ansys.jinja2")
539
+
540
+ dest_file = dest_dir / "ansys.jinja2"
541
+ assert dest_file.exists()
542
+ assert not dest_file.is_symlink()
543
+ assert dest_file.read_text() == "template content"
544
+
545
+
546
+ @pytest.mark.add_license_headers
547
+ def test_mkdirs_and_link_skips_existing_symlink(tmp_path):
548
+ """Test that mkdirs_and_link skips creation when symlink points to the correct source."""
549
+ src_dir = tmp_path / "hook_assets"
550
+ src_dir.mkdir()
551
+ src_file = src_dir / "ansys.jinja2"
552
+ src_file.write_text("template content")
553
+
554
+ dest_dir = tmp_path / "repo" / ".reuse" / "templates"
555
+ dest_dir.mkdir(parents=True)
556
+ dest_file = dest_dir / "ansys.jinja2"
557
+
558
+ try:
559
+ dest_file.symlink_to(src_file)
560
+ except OSError:
561
+ pytest.skip("Symlinks not supported on this platform/environment")
562
+
563
+ mtime_before = src_file.stat().st_mtime
564
+
565
+ hook.mkdirs_and_link(str(dest_dir), str(src_dir), str(dest_dir), "ansys.jinja2")
566
+
567
+ # File should still exist and be unchanged
568
+ assert dest_file.is_symlink()
569
+ assert dest_file.read_text() == "template content"
570
+ assert src_file.stat().st_mtime == mtime_before
571
+
572
+
573
+ @pytest.mark.add_license_headers
574
+ def test_add_header_skips_unknown_comment_style(tmp_path, monkeypatch):
575
+ """Test add_header skips files when no comment style can be inferred."""
576
+ test_file = tmp_path / "unknown.ext"
577
+ test_file.write_text("content\n", encoding="utf-8")
578
+
579
+ called = {"add_header_to_file": False}
580
+
581
+ def _fake_add_header_to_file(**_kwargs):
582
+ called["add_header_to_file"] = True
583
+
584
+ monkeypatch.setattr("reuse.cli.annotate.get_comment_style", lambda _path: None)
585
+ monkeypatch.setattr("reuse.cli.annotate.add_header_to_file", _fake_add_header_to_file)
586
+
587
+ out = io.StringIO()
588
+ hook.add_header(
589
+ [DEFAULT_COPYRIGHT],
590
+ ["MIT"],
591
+ f"{dt.today().year}",
592
+ str(test_file),
593
+ "ansys.jinja2",
594
+ True,
595
+ out,
596
+ )
597
+
598
+ assert not called["add_header_to_file"]
599
+ assert "Skipping" in out.getvalue()
600
+
601
+
499
602
  @pytest.mark.add_license_headers
500
603
  def test_bad_chars(tmp_path: pytest.TempPathFactory):
501
604
  # Set template and license names
@@ -847,6 +950,46 @@ def test_mit_header_replaced_with_apache(tmp_path: pytest.TempPathFactory):
847
950
  os.chdir(REPO_PATH)
848
951
 
849
952
 
953
+ @pytest.mark.add_license_headers
954
+ def test_no_duplicate_prints_on_license_switch(
955
+ tmp_path: pytest.TempPathFactory, capsys: pytest.CaptureFixture
956
+ ):
957
+ """Test that switching from MIT to Apache-2.0 prints each changed file exactly once."""
958
+ template_name = "ansys.jinja2"
959
+ license_name = "Apache-2.0.txt"
960
+ template_path = Path(REPO_PATH) / ".reuse" / "templates" / template_name
961
+ license_path = (
962
+ Path(REPO_PATH)
963
+ / "src"
964
+ / "ansys"
965
+ / "pre_commit_hooks"
966
+ / "assets"
967
+ / "LICENSES"
968
+ / license_name
969
+ )
970
+
971
+ repo, tmp_file = set_up_repo(tmp_path, template_path, template_name, license_path, license_name)
972
+
973
+ # First run: add the MIT header
974
+ add_argv_run(repo, tmp_file, [tmp_file])
975
+ repo.index.add([tmp_file])
976
+ capsys.readouterr() # discard output from the MIT-header run
977
+
978
+ # Second run: switch to Apache-2.0 — triggers the license-switch branch
979
+ add_argv_run(repo, tmp_file, [tmp_file, "--custom_license=Apache-2.0"])
980
+
981
+ captured = capsys.readouterr()
982
+ file_lines = [
983
+ line for line in captured.out.splitlines() if "Successfully changed header" in line
984
+ ]
985
+ assert len(file_lines) == 1, (
986
+ f"Expected 'Successfully changed header' to appear exactly once, "
987
+ f"but got {len(file_lines)} time(s):\n{captured.out}"
988
+ )
989
+
990
+ os.chdir(REPO_PATH)
991
+
992
+
850
993
  @pytest.mark.add_license_headers
851
994
  def test_mit_license_file_replaced_with_apache(tmp_path: pytest.TempPathFactory):
852
995
  """Test that LICENSE file is regenerated as Apache-2.0 when it's the custom license."""
@@ -888,6 +1031,34 @@ def test_mit_license_file_replaced_with_apache(tmp_path: pytest.TempPathFactory)
888
1031
  os.chdir(REPO_PATH)
889
1032
 
890
1033
 
1034
+ @pytest.mark.add_license_headers
1035
+ def test_apache_license_year_update_preserves_boilerplate(tmp_path):
1036
+ """Test that updating the year in an Apache-2.0 LICENSE does not corrupt the boilerplate.
1037
+
1038
+ Regression test: the 'January 2004' text in the Apache boilerplate header must not
1039
+ be replaced with the current year when the copyright year is updated.
1040
+ """
1041
+ hook_loc = Path(REPO_PATH) / "src" / "ansys" / "pre_commit_hooks"
1042
+ template_dir = hook_loc / "assets" / "LICENSES"
1043
+ license_file = tmp_path / "LICENSE"
1044
+
1045
+ # Generate an Apache-2.0 LICENSE file with a fixed start year
1046
+ hook.generate_license_file(template_dir, "2023", license_file, "Apache-2.0")
1047
+
1048
+ initial_content = license_file.read_text(encoding="utf-8")
1049
+ assert "January 2004" in initial_content
1050
+ assert "Copyright 2023 ANSYS" in initial_content
1051
+
1052
+ # Simulate a year-span update (e.g., a new calendar year)
1053
+ hook.update_license_file(license_file, "2023 - 2026", "Apache-2.0")
1054
+
1055
+ updated_content = license_file.read_text(encoding="utf-8")
1056
+ assert (
1057
+ "January 2004" in updated_content
1058
+ ), "The Apache boilerplate 'January 2004' must not be overwritten by the year update"
1059
+ assert "Copyright 2023 - 2026 ANSYS" in updated_content
1060
+
1061
+
891
1062
  @pytest.mark.add_license_headers
892
1063
  def test_line_endings(tmp_path: pytest.TempPathFactory):
893
1064
  """Test line endings remain the same before and after running the hook."""