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.
- 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
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
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.
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.
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.
- 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
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
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.
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.
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.
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.
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).
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.
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).
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.
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.
- 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
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().
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.
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.
- 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
- 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()
- 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
- 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
- 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
- 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
Add Google-style "Why" docstrings to classes that lacked documentation:
base models, BaseEntry, BulletEntry, BaseEducationEntry,
CustomConnection, RenderCVModel, and all exception dataclasses.
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
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.
The rich progress panel outputs ✓ (U+2713), which Windows cp1252
console encoding cannot encode. Set PYTHONIOENCODING=utf-8 in the
subprocess environment.