Skip to content

Update iCalendar generation tool: add output filename template support#3679

Open
KJhellico wants to merge 5 commits into
vacanza:devfrom
KJhellico:upd-generate-ics
Open

Update iCalendar generation tool: add output filename template support#3679
KJhellico wants to merge 5 commits into
vacanza:devfrom
KJhellico:upd-generate-ics

Conversation

@KJhellico

Copy link
Copy Markdown
Collaborator

Proposed change

Add --output-template to generate output filenames dynamically (e.g., "{code}_{subdiv}_{language}_{start_year}_{end_year}_{categories}.ics")

Placeholder Description
{code} Country or financial market code
{subdiv} Subdivision code, or ALL if not specified
{language} Requested language, or DEFAULT if not specified
{categories} Holiday categories joined with _, or PUBLIC if not specified
{start_year} First year
{end_year} Last year
{today} Current date in YYYYMMDD format

Closes #3642, #3645.

Type of change

  • New country/market holidays support (thank you!)
  • Supported country/market holidays update (calendar discrepancy fix, localization)
  • Existing code/documentation/test/process quality improvement (best practice, cleanup, refactoring, optimization)
  • Dependency update (version deprecation/pin/upgrade)
  • Bugfix (non-breaking change which fixes an issue)
  • Breaking change (a code change causing existing functionality to break)
  • New feature (new holidays functionality in general)

Checklist

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 198a66a0-7c52-406e-a80a-d9607e216c4c

📥 Commits

Reviewing files that changed from the base of the PR and between 8b46fac and dd29d03.

📒 Files selected for processing (2)
  • holidays/generate_ics.py
  • tests/test_generate_ics.py

Summary by CodeRabbit

  • New Features
    • Added --output-template to generate .ics output filenames using str.format-style placeholders, including automatic default templates and multi-year support.
  • Documentation
    • Updated examples and added an “Output filename templates” section listing supported placeholders and how to escape literal braces ({{ / }}).
  • Bug Fixes
    • Improved handling of invalid templates and unknown placeholders with clearer, deterministic error messages.
  • Tests
    • Expanded assertions for filename rendering (including {today}), escaped-brace behavior, and exact CLI error/output results.

Walkthrough

Adds --output-template support to holidays-ics, with default filename templates that include subdivision, language, categories, and year-range fields. The generator now validates and formats templates, and tests and docs cover the new filename behavior.

Changes

Output filename templating

Layer / File(s) Summary
CLI argument and default template
holidays/generate_ics.py
Adds --output-template and get_default_output_template() for building default filename templates from selected inputs.
Placeholder substitution and validation in run()
holidays/generate_ics.py
Adds template validation helpers and replaces run() output-path construction with template selection, placeholder value mapping, and formatted output path generation.
Filename and template tests
tests/test_generate_ics.py
Extends coverage for filename construction, template rendering, escaping, {today}, and validation errors.
Documentation updates for output templates
docs/examples.md
Documents --output-template, supported placeholders, escaping syntax, and updated examples.

Estimated code review effort: 2 (Simple) | ~15 minutes

Possibly related PRs

  • vacanza/holidays#3593: Both PRs modify holidays/generate_ics.py output filename generation logic.
  • vacanza/holidays#3640: Both PRs touch filename construction in holidays/generate_ics.py and related tests around year-range inputs.

Suggested labels: script

Suggested reviewers: PPsyrius

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly matches the main change: adding output filename template support to iCalendar generation.
Description check ✅ Passed The description directly describes the template-based filename change and its placeholders, matching the PR.
Linked Issues check ✅ Passed The changes satisfy #3642 by automatically including categories/language context in generated filenames and updating the examples docs.
Out of Scope Changes check ✅ Passed The docs and tests are aligned with the new filename-template feature and do not appear unrelated to the stated goals.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@KJhellico KJhellico linked an issue Jul 5, 2026 that may be closed by this pull request
@codecov

codecov Bot commented Jul 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (5a02fa3) to head (dd29d03).

Additional details and impacted files
@@            Coverage Diff            @@
##               dev     #3679   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files          318       318           
  Lines        19167     19210   +43     
  Branches      2457      2471   +14     
=========================================
+ Hits         19167     19210   +43     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/examples.md`:
- Around line 719-723: The CLI example contains a typo in the holidays-ics
command: the output template flag is written with three leading dashes instead
of the standard flag form. Update the example in the docs snippet so the
`holidays-ics` invocation uses the correct `--output-template` option, matching
the other arguments like `--subdiv` and `--language`.

In `@holidays/generate_ics.py`:
- Around line 227-250: The output template handling in generate_ics.py only
catches KeyError, so malformed templates can still crash with an unhandled
traceback. Update the template.format(**values) exception handling in the
output-path logic to also catch format parsing errors such as ValueError and
IndexError, and raise the same SystemExit with a friendly message from the
original exception using exception chaining. Use the existing generate_ics path
that builds output_path and the values/template variables to keep the fix
localized.

In `@tests/test_generate_ics.py`:
- Around line 459-507: Add a test for a malformed output template with an
unmatched brace (for example in IcsGenerator().run via the existing
test_output_template_* patterns) to cover the uncaught ValueError/IndexError
path in output template parsing. Keep the test in unittest.TestCase style using
self.assertRaises/SystemExit conventions, and place it alongside the existing
output template tests so it exercises the same IcsGenerator and
template-handling code path.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: eb08fb84-093b-41ca-8cbd-7af7d73dc23b

📥 Commits

Reviewing files that changed from the base of the PR and between 5a02fa3 and 797c7ef.

📒 Files selected for processing (3)
  • docs/examples.md
  • holidays/generate_ics.py
  • tests/test_generate_ics.py

Comment thread docs/examples.md
Comment thread holidays/generate_ics.py Outdated
Comment thread tests/test_generate_ics.py Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 3 files

Confidence score: 3/5

  • In holidays/generate_ics.py, malformed --output-template values can raise an uncaught formatting exception because only KeyError is handled in the new formatter path, so users may see a crash instead of a clear CLI validation message. Catch the relevant format-string exceptions and return a user-friendly argparse-style error before merging.
  • In docs/examples.md, the example uses ---output-template (three dashes), which will fail when copied because the CLI flag is invalid. Update the docs to --output-template so users can run the example successfully.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread holidays/generate_ics.py Outdated
Comment thread docs/examples.md Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
holidays/generate_ics.py (2)

196-197: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Chain the exception with from e.

Ruff B904 flags this: re-raising SystemExit from within the except ValueError block loses the original traceback context.

🛠️ Proposed fix
         except ValueError as e:
-            raise SystemExit(f"Invalid output template: {e}")
+            raise SystemExit(f"Invalid output template: {e}") from e
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/generate_ics.py` around lines 196 - 197, The exception handling in
the output template validation block loses traceback context when `ValueError`
is re-raised as `SystemExit`. Update the `except ValueError as e` path in
`generate_ics.py` to chain the new `SystemExit` from the original exception
using `from e` so the original error context is preserved.

Source: Linters/SAST tools


265-267: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

template.format(**values) is unguarded — malformed format specs still crash ugly.

validate_output_template only calls Formatter().parse(), which tokenizes the template but does not validate format specs against the field's __format__ implementation. A template like "{code:d}" or "{categories:s}" (mismatched spec/type) passes validation cleanly but raises an unhandled ValueError at line 267, since only the outer entity_loader/save_ics block (lines 269-279) has a try/except. This is the same root concern from the earlier review (only KeyError/parse-time errors are handled) that wasn't fully closed off by adding the ValueError catch inside validate_output_template.

🛡️ Proposed fix
             self.validate_output_template(set(values))
             template = self.args.output_template or self.get_default_output_template()
-            output_path = template.format(**values)
+            try:
+                output_path = template.format(**values)
+            except (KeyError, IndexError, ValueError) as e:
+                raise SystemExit(f"Invalid output template {template!r}: {e}") from e
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/generate_ics.py` around lines 265 - 267, The output template
rendering in generate_ics is still unguarded because validate_output_template
only parses the template and does not catch format-spec mismatches that fail
inside template.format(**values). Update generate_ics so the formatting step
around template.format(**values) is protected against ValueError and similar
template rendering errors, and surface a clear validation/error path instead of
letting the exception escape; use the existing validate_output_template and
output_template flow as the entry point for the fix.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@holidays/generate_ics.py`:
- Around line 196-197: The exception handling in the output template validation
block loses traceback context when `ValueError` is re-raised as `SystemExit`.
Update the `except ValueError as e` path in `generate_ics.py` to chain the new
`SystemExit` from the original exception using `from e` so the original error
context is preserved.
- Around line 265-267: The output template rendering in generate_ics is still
unguarded because validate_output_template only parses the template and does not
catch format-spec mismatches that fail inside template.format(**values). Update
generate_ics so the formatting step around template.format(**values) is
protected against ValueError and similar template rendering errors, and surface
a clear validation/error path instead of letting the exception escape; use the
existing validate_output_template and output_template flow as the entry point
for the fix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6ec01a97-1fe7-4e66-8906-b9a9798e8aa2

📥 Commits

Reviewing files that changed from the base of the PR and between 797c7ef and 3854ada.

📒 Files selected for processing (3)
  • docs/examples.md
  • holidays/generate_ics.py
  • tests/test_generate_ics.py

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="holidays/generate_ics.py">

<violation number="1" location="holidays/generate_ics.py:186">
P2: Some invalid output templates still crash with an uncaught formatting exception instead of the new validation error. `Formatter().parse()` exposes nested placeholders in `format_spec`, so validating only `field_name` misses cases like `{code:{bad}}`; consider validating nested format specs or catching `KeyError`/`ValueError` around the final `template.format(**values)`.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread holidays/generate_ics.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
tests/test_generate_ics.py (1)

459-507: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Missing coverage for pure-literal escaped braces.

Good coverage of custom templates, defaults, {today}, and {{{code}}}} escaping-around-a-real-placeholder. Consider adding a case like --output-template "{{literal}}.ics" (escaped braces with no real placeholder inside) to catch the field-extraction bug flagged in holidays/generate_ics.py (Line 188), where such templates incorrectly raise "Unknown placeholder".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_generate_ics.py` around lines 459 - 507, Add a test in
test_generate_ics for a pure-literal escaped-brace template such as
"{{literal}}.ics" to cover the field-extraction path in IcsGenerator.run. The
issue is that generate_ics currently treats escaped braces with no real
placeholder as an unknown placeholder, so the new test should assert the
template is accepted and the expected literal-named file is created. Use the
existing test_output_template_with_braces and
test_output_template_unknown_placeholder cases as nearby references when adding
the new coverage.
holidays/generate_ics.py (1)

180-199: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Escaped literal braces are parsed as placeholders. re.findall(r"\{([a-z_]+)\}", template) will treat {{report}}.ics as containing {report}, so a valid literal template is rejected as an unknown placeholder; derive placeholder names from the same token grammar used in the fullmatch check.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@holidays/generate_ics.py` around lines 180 - 199, The output template
validation in validate_output_template is misclassifying escaped literal braces
as real placeholders. Update the placeholder extraction logic to use the same
token grammar as the re.fullmatch check, so templates like double-braced
literals are ignored instead of being passed into the unknown-placeholder
validation. Keep the existing supported-placeholder error path, but ensure only
actual {name} tokens are collected from the template.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@holidays/generate_ics.py`:
- Around line 180-199: The output template validation in
validate_output_template is misclassifying escaped literal braces as real
placeholders. Update the placeholder extraction logic to use the same token
grammar as the re.fullmatch check, so templates like double-braced literals are
ignored instead of being passed into the unknown-placeholder validation. Keep
the existing supported-placeholder error path, but ensure only actual {name}
tokens are collected from the template.

In `@tests/test_generate_ics.py`:
- Around line 459-507: Add a test in test_generate_ics for a pure-literal
escaped-brace template such as "{{literal}}.ics" to cover the field-extraction
path in IcsGenerator.run. The issue is that generate_ics currently treats
escaped braces with no real placeholder as an unknown placeholder, so the new
test should assert the template is accepted and the expected literal-named file
is created. Use the existing test_output_template_with_braces and
test_output_template_unknown_placeholder cases as nearby references when adding
the new coverage.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: d074857a-45ea-49fa-9d08-7585fe19a116

📥 Commits

Reviewing files that changed from the base of the PR and between 3854ada and e939b4d.

📒 Files selected for processing (2)
  • holidays/generate_ics.py
  • tests/test_generate_ics.py

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 2 files (changes from recent commits).

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread holidays/generate_ics.py Outdated
Comment thread holidays/generate_ics.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@holidays/generate_ics.py`:
- Around line 185-189: The placeholder-name regex is duplicated in the template
parsing logic, so update the `generate_ics.py` template handling to define a
single shared module-level constant for the `{name}` pattern and reuse it in
both the `re.findall` tokenizer and the `fields` extraction. Keep the `tokens`
validation and `fields` parsing behavior in sync by referencing that shared
regex from the existing template-processing code.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: fbc311ec-04f2-409a-95e1-9f2ada6f6768

📥 Commits

Reviewing files that changed from the base of the PR and between e939b4d and 8b46fac.

📒 Files selected for processing (1)
  • holidays/generate_ics.py

Comment thread holidays/generate_ics.py Outdated
@sonarqubecloud

sonarqubecloud Bot commented Jul 5, 2026

Copy link
Copy Markdown

@PPsyrius PPsyrius left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 🛠️

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

--output-suffix flag to modify default output filename ending Include categories/language in default filenames

3 participants