1961 Commits

Author SHA1 Message Date
Sina Atalay
1d4b87bc42 Fix date validation test to work across Python 3.12-3.14
Python 3.14 changed ValueError messages for invalid dates.
Use broader match patterns that work across all versions.
2026-03-25 16:45:27 +03:00
Sina Atalay
b643d456e7 Fix flaky Hypothesis test for case-sensitive keyword matching
Some Unicode uppercase letters (e.g., mathematical bold 𝐀) have no
distinct lowercase form, so keyword.lower() == keyword. Filter these
out since the test requires a genuine case difference.
2026-03-25 16:38:48 +03:00
Sina Atalay
6956f39835 Address code review feedback on Hypothesis and classic_theme changes
- Update hypothesis to latest version (>=6.151.9)
- Remove pythonpath pytest config (was only needed for tests.strategies)
- Consolidate classic_theme.py into single file with all design models
- Move Hypothesis strategies from strategies.py into their test files
- Add noqa: ARG001 to unused yaml_field_override CLI parameter
- Fix lint and type errors across the codebase
2026-03-25 16:28:44 +03:00
Sina Atalay
2df9d2262b Fix placeholder removal eating provided placeholders with overlapping names
The regex in remove_not_provided_placeholders used a bare alternation
without word boundaries, so removing placeholder "AA" would also
destroy "AAA" in the same template. Fix: add \b word boundaries and
sort longest-first.

Also add Hypothesis tests for:
- remove_not_provided_placeholders: provided keys survive removal
- remove_connectors_of_missing_placeholders: connectors removed/preserved
- validate_arbitrary_date: pass-through for valid dates and custom text
- Mastodon URL: domain and username appear in generated URL
2026-03-25 16:28:44 +03:00
Sina Atalay
141ea9fe92 Fix bold_keywords not matching keywords with non-word boundaries (#706)
The \b word boundary anchor requires a word character (\w) on one side
and a non-word character on the other. Keywords ending with ":" or
starting with "+" have no word boundary at those positions, so \b
fails to match.

Fix: only add \b on the side of a keyword where the character is a
word character. For "Tech stack:", \b is added before "T" but not
after ":". For "C++", \b is added before "C" but not after "+".

Added a Hypothesis property test that catches this entire class of
bugs: any keyword surrounded by spaces in the input text must appear
bolded in the output.
2026-03-25 16:28:44 +03:00
Sina Atalay
005d1d835f Remove useless Hypothesis tests that fuzz trivial operations
Removed tests that were running 100 random inputs through string
operations that can never fail regardless of input:
- process_highlights: fuzzing "- " + string prepend
- process_authors: fuzzing ", ".join()
- PublicationEntry DOI URL: fuzzing f"https://doi.org/{doi}"

These tests add execution time without any chance of finding bugs.
2026-03-25 16:28:20 +03:00
Sina Atalay
1704a4be57 Add Hypothesis test for PublicationEntry DOI URL generation
Test that random valid DOI strings always produce the correct
https://doi.org/ URL. Removed the useless test_doi_clears_url that
was just re-verifying a static validator with random irrelevant input.
2026-03-25 16:28:20 +03:00
Sina Atalay
aa4c47e3a1 Move Hypothesis TypstDimension tests to test_typst_dimension.py
They were in the wrong file (test_classic_theme.py). Now they live
alongside the existing parametrized dimension tests.
2026-03-25 16:28:20 +03:00
Sina Atalay
9bf120f07b Add Hypothesis test for bold_keywords deduplication order preservation 2026-03-25 16:28:20 +03:00
Sina Atalay
34d4055aef Add Hypothesis tests for dictionary_key_to_proper_section_title
- snake_case keys always replace underscores with spaces
- keys with spaces returned unchanged
- keys with uppercase letters returned unchanged
2026-03-25 16:28:20 +03:00
Sina Atalay
3728cd5e33 Add Hypothesis tests for BaseEntryWithComplexFields date adjustment logic
- date_only_clears_start_and_end: providing date nullifies start/end
- start_only_implies_present: start without end defaults to "present"
- end_only_becomes_date: end without start treated as single date
2026-03-25 16:28:20 +03:00
Sina Atalay
427053339b Add Hypothesis tests for process_highlights, process_authors, clean_trailing_parts
- process_highlights: bullet count matches input length, output starts with "- "
- process_authors: comma count is n-1, all authors appear in output
- clean_trailing_parts: allowed trailing chars preserved, never crashes
2026-03-25 16:28:20 +03:00
Sina Atalay
0f7f6f4f97 Follow test naming conventions for Hypothesis tests
Merge property-based tests into existing test classes per the project
testing rules: multiple tests for the same function belong in a
TestFunctionName class.

- TestEscapeTypstCharactersProperties merged into TestEscapeTypstCharacters
- TestMarkdownToTypstProperties merged into TestMarkdownToTypst
- TestBuildDatePlaceholdersProperties merged into TestBuildDatePlaceholders
- TestComputeTimeSpanStringProperties merged into TestComputeTimeSpanString
- TestGetDateObjectProperties renamed to TestGetDateObject
- TestApplyOverridesProperties merged into TestApplyOverridesToDictionary
- TestUpdateValueByLocationProperties merged into TestUpdateValueByLocation
- TestResolveOutputFolderPlaceholderProperties merged into TestResolveOutputFolderPlaceholder
- TestBuildNameVariantsProperties renamed to TestBuildNameVariants
- TestTypstDimensionProperties renamed to TestTypstDimension
- TestSocialNetworkUsernameProperties merged into TestSocialNetwork
2026-03-25 16:28:20 +03:00
Sina Atalay
5e95eee87d Distribute Hypothesis tests into their respective unit test files
Move all 55 property-based tests from tests/test_hypothesis.py into
the existing test files for each module they test:

- test_string_processor.py: keyword bolding, placeholder, URL, pattern
- test_markdown_parser.py: Typst escaping, markdown-to-typst
- test_date.py: date parsing, placeholders, time spans
- test_override_dictionary.py: immutability, path traversal
- test_path_resolver.py: name variants, OUTPUT_FOLDER resolution
- test_classic_theme.py: Typst dimension validation
- test_social_network.py: username format validation

Reusable Hypothesis strategies live in tests/strategies.py. Added
pythonpath=["."] to pyproject.toml so tests can import the strategies
module.
2026-03-25 16:28:20 +03:00
Sina Atalay
7034a5e70b Fix 3 bugs found by Hypothesis property-based testing
1. clean_url: use rstrip("/") instead of single-slash strip. URLs
   with multiple trailing slashes (e.g., "example.com//") now clean
   fully. Also restores idempotency (clean(clean(x)) == clean(x)).

2. get_date_object: use Date(year, 1, 1) instead of
   Date.fromisoformat(f"{date}-01-01"). The fromisoformat call
   crashed on years < 1000 because it requires 4-digit year strings.

3. build_date_placeholders: use f"{year % 100:02d}" for
   YEAR_IN_TWO_DIGITS instead of str(year)[-2:]. The slice produced
   1-char output for single-digit years (e.g., year 9 gave "9"
   instead of "09").

Test ranges widened back to full datetime.date range now that the
source code handles all values correctly.
2026-03-25 16:28:20 +03:00
Sina Atalay
5fb507d388 Add Hypothesis property-based testing suite (55 tests)
Property-based tests verify invariants across random inputs, catching
edge cases that hand-picked parametrized tests miss. Covers 6 modules:

- string_processor: keyword bolding, placeholder substitution, URL cleaning
- markdown_parser: Typst escaping robustness, formatting preservation
- date: date parsing, placeholder generation, time span arithmetic
- override_dictionary: immutability, path traversal, error conditions
- path_resolver: name variant generation, OUTPUT_FOLDER resolution
- Pydantic validators: Typst dimensions, social network usernames

Bugs surfaced during development:
- clean_url only strips one trailing slash (double slash passes through)
- get_date_object crashes on year < 10 (single digit isoformat)
- YEAR_IN_TWO_DIGITS is 1 char for years < 10
2026-03-25 16:28:20 +03:00
Sina Atalay
41b51774c7 Rename override_hint to yaml_field_override for clarity 2026-03-25 16:28:20 +03:00
Sina Atalay
43b255eff7 Split classic_theme.py into focused modules
Extract three modules from classic_theme.py (845 lines) to improve
navigability:

- templates.py: entry template option models and Templates class
- typography.py: Typography, FontFamily config, FontSize, SmallCaps, Bold
- header.py: Header, Connections, Links, PhoneNumberFormatType

classic_theme.py retains Page, Colors, Sections, Entries, and
ClassicTheme. All imports use absolute paths so the custom theme
generator (which copies classic_theme.py as text) continues to work.

Template entry option classes renamed from e.g. OneLineEntry to
OneLineEntryTemplate to avoid name collision with CV entry models.
2026-03-25 16:28:20 +03:00
Sina Atalay
8822e75c04 Narrow bare Exception catch in PyPI version fetcher
Replace except Exception with specific exception types: OSError
(covers URLError and network issues), json.JSONDecodeError,
KeyError, and ValueError. The function still returns None on
any expected failure, but won't silently swallow programming
errors like TypeError or AttributeError.
2026-03-25 16:28:20 +03:00
Sina Atalay
bc7f999979 Rename _ dummy parameter to override_hint
The underscore-prefixed name violated the project convention of no
private API syntax. Renamed to override_hint which better describes
its purpose (showing override syntax in --help output).
2026-03-25 16:28:20 +03:00
Sina Atalay
705b4a132b Replace global monkey-patch with per-instance Scanner override
Set yaml.Scanner on the YAML instance instead of overwriting
ruamel.yaml.scanner.RoundTripScanner globally. This avoids
affecting other code that imports ruamel.yaml and is resilient
to library version changes.
2026-03-25 16:28:20 +03:00
Sina Atalay
7dc1e02e86 Replace deprecated urlretrieve with urlopen for photo downloads
urlretrieve is semi-deprecated. Use urlopen with explicit write,
matching the pattern already used in app.py. Also narrows the
except clause from bare Exception to (URLError, OSError).
2026-03-25 16:28:20 +03:00
Sina Atalay
304df0350f Extract comment_out_section_sub_fields helper in sample_generator
The YAML string manipulation that comments out design and locale
sub-fields was inline and duplicated. Extracted into a named helper
with clear docstring explaining the splitting/commenting/reassembling
strategy. Uses str.partition() instead of str.split() for safer
two-way splitting.
2026-03-25 16:28:20 +03:00
Sina Atalay
d16cc8ecde Extract duplicate nested bullet expansion into helper function
The same regex-based YAML line processing appeared in both
create_sample_yaml_input_file and create_sample_yaml_file.
Extracted into expand_nested_bullets() to eliminate duplication.
2026-03-25 16:28:20 +03:00
Sina Atalay
2a8e7fc6b1 Add missing return type annotations to three functions
- json_schema_generator: generate() now has CoreSchema, JsonSchemaMode,
  and JsonSchemaValue annotations
- yaml_reader: fetch_alias() now returns None
- sample_generator: str_representer() now has Representer and ScalarNode
  annotations
2026-03-25 16:28:20 +03:00
Sina Atalay
5f4f28fd4c Remove commented-out page_break_before field and validator reference
Dead code that was commented out. Can be restored from git history
if needed in the future.
2026-03-25 16:28:20 +03:00
Sina Atalay
87345f0dcf Fix redundant type annotation in entry_templates_from_input.py
dict[str, str | str] was redundant since str | str simplifies to str.
2026-03-25 16:28:20 +03:00
Sina Atalay
5eac9641aa Use pathlib read_text with tomllib.loads instead of open()
Replace the file handle pattern with pathlib's read_text and
tomllib.loads for cleaner TOML parsing.
2026-03-25 16:28:20 +03:00
Sina Atalay
573af4a9f1 Move WhatsApp phone validator to inline construction
Only built when WhatsApp validation actually runs, avoiding
module-level initialization overhead for all other social networks.
2026-03-25 16:28:20 +03:00
Sina Atalay
26e9c6d86f Revert current_date type to Any in ValidationContext
The field receives raw YAML data from commented_map.get() which can
be anything the user provides, not just Date | str | None. The
get_current_date function already handles arbitrary input defensively
with isinstance checks and falls back to Date.today().
2026-03-25 16:28:20 +03:00
Sina Atalay
441214e8c8 Remove ty: ignore in process_url by converting url to str
HttpUrl from Pydantic is cleanly converted to str before passing
to clean_url, eliminating the type checker suppress comment.
2026-03-25 16:28:20 +03:00
Sina Atalay
2552e18c96 Extract resolve_nested_model helper to reduce cast() duplication
Consolidates the repeated pattern of resolving a FieldInfo's default
to a Pydantic BaseModel instance (via default or default_factory) into
a single helper. Also tightens update_description_with_new_default
parameter types from Any to object.
2026-03-25 16:28:20 +03:00
Sina Atalay
0857fc1d1b Decompose cli_command_new by extracting build_creation_panel
The panel-building logic (Rich panel with file statuses, next steps,
and template info) is now a separate function, keeping the command
handler focused on validation and file creation orchestration.
2026-03-25 16:28:20 +03:00
Sina Atalay
662a3748c0 Exclude vendored typst_fontawesome from formatter and type checker
ruff already excluded it. Add exclusions for black ([tool.black]
extend-exclude) and ty ([tool.ty.src] exclude) in pyproject.toml.
2026-03-25 16:28:20 +03:00
Sina Atalay
db6ad98654 Fix test failures from code quality changes
- Revert entry_with_date.py to let ValueError propagate naturally
  (Pydantic produces better messages like "The month must be between
  1 and 12." than our generic wrapper)
- Update create_theme tests to expect typer.Exit (now has error handler)
- Update pdf_png test to use real temp file instead of patching read_text
- Fix watcher test mock to handle double join() call pattern
- Regenerate JSON schema after union type order change
2026-03-25 16:28:20 +03:00
Sina Atalay
ec884975d4 Fix lint issues from previous changes
- Use Path.open() instead of open() in pdf_png.py (PTH123)
- Chain KeyError with 'from e' in pdf_png.py (B904)
- Fix comment placement in section.py to avoid false unused-ignore
- Update watcher test to mock observer.join() instead of time.sleep()
2026-03-25 16:28:20 +03:00
Sina Atalay
515e423817 Improve type safety: remove suppression and document unavoidable ones
- Replace ty:ignore with proper type narrowing in run_rendercv.py
- Add explanatory comments to all remaining ty:ignore suppressions
  in entry_templates_from_input.py, override_dictionary.py,
  section.py, and design.py explaining why each is safe
2026-03-25 16:28:20 +03:00
Sina Atalay
dd90554764 Improve code quality: dedup, state safety, and modern patterns
- Reset markdown parser state before each convert() call
- Avoid loop variable reassignment in model_processor.py
- Extract build_name_variants() to deduplicate path_resolver.py
- Move WhatsApp phone TypeAdapter to module level (was per-call)
- Use tomllib for TOML parsing instead of string splitting
- Replace busy-wait loop with observer.join() in watcher.py
2026-03-25 16:28:20 +03:00
Sina Atalay
49384c8889 Fix safety and correctness issues across codebase
- Narrow warnings.filterwarnings to pydantic module only (was global)
- Remove ssl._create_unverified_context() private API usage
- Wrap Date.fromisoformat() in try/except for invalid calendar dates
- Preserve insertion order in bold_keywords dedup (dict.fromkeys)
- Guard reduce() with initial value to prevent crash on empty list
  in built_in_design.py and locale.py
- Document that negative Typst dimensions are intentional
2026-03-25 16:28:20 +03:00
Sina Atalay
09aa20ed51 Add missing return type annotations, fix typing, and add error handler
- Add -> None return types to cli_command_render, run_rendercv,
  print_welcome, cli_command_create_theme
- Add @handle_user_errors to create-theme command (was the only
  command missing it)
- Replace Any with Date | str | None in ValidationContext.current_date
- Remove unused type variable T from handle_user_errors
2026-03-25 16:28:20 +03:00
Sina Atalay
ef19838ce8 Add missing docstrings to base models, entry types, and exceptions
Add Google-style "Why" docstrings to classes that lacked documentation:
base models, BaseEntry, BulletEntry, BaseEducationEntry,
CustomConnection, RenderCVModel, and all exception dataclasses.
2026-03-25 16:28:20 +03:00
Francesco P. Carmone
e75f1c7d4e Updated pattern matching algorithm, see issue##706 (#709) 2026-03-25 15:42:45 +03:00
Sina Atalay
523e6e2f0d Fix phone number docs: quote example and mention format options
The phone number example was unquoted, causing YAML to interpret it as
an integer. Also added a note about design.header.connections.phone_number_format
for controlling the display format (national/international/E164).

Ref: https://github.com/rendercv/rendercv/discussions/707
2026-03-24 16:42:49 +03:00
Sina Atalay
1ffb4c4890 Add flag_emoji property to locale model
Add a cached_property that maps each language to its primary country's
flag emoji using regional indicator symbols, with tests and updated
developer guide.
2026-03-23 03:20:53 +03:00
Sina Atalay
d475aab312 Fix logo and title hover effect to only apply when not scrolled 2026-03-21 21:27:54 +03:00
Sina Atalay
e8eb000421 Better branding in the documentation 2026-03-21 19:21:43 +03:00
Sina Atalay
9387166637 Fix offline wheel test Unicode crash on Windows
The rich progress panel outputs ✓ (U+2713), which Windows cp1252
console encoding cannot encode. Set PYTHONIOENCODING=utf-8 in the
subprocess environment.
2026-03-21 15:15:16 +03:00
Sina Atalay
56c30dc605 Show stdout/stderr in offline wheel test failures for better diagnostics 2026-03-21 15:11:28 +03:00
Sina Atalay
617dcf779f Fix offline wheel test failing on Windows due to hardcoded Unix venv path 2026-03-21 14:54:44 +03:00
Sina Atalay
dd5ac9115a Fix test_raises_error_when_version_missing_from_typst_toml match pattern 2026-03-21 03:56:52 +03:00