One useful idea
A regular expression describes a text shape. Here the contract is deliberately narrower than Module 1: project and shot each start with an ASCII letter, then contain at most 31 more ASCII letters, digits or hyphens. Underscores separate fields; they cannot occur inside them. The whole name is project_shot_vVERSION.extension. A parser cannot guess which underscore belongs to a project.
Import re, compile a pattern once, then call pattern.fullmatch(name). fullmatch consumes the entire input; a search for a substring could accept an unsafe path or backup suffix. A raw Python string such as r"\." keeps the backslash for the regex engine; \. matches a literal dot, not any character. [0-9] means ASCII digits; {2,3} means two or three repetitions. (?P<version>...) names a capture, and match.groupdict() returns the named text fields. Check for None before reading groups.
Shape alone is not policy. Convert version text with int, require 1 through 999, then compare the original digits with f"{version:02d}": 01–99 use two digits, 100–999 use three. Thus v1, v00 and v001 are refused, rather than silently rewritten. The extension is mp4/mov/mkv/webm with ASCII case variants and returns lowercase; project/shot keep their supplied case.
Use reviewed bounded_text to refuse wrong types, excessive length, invalid controls and surrogates. The name limit is 120 code points; component limits remain 32. Refuse Windows device components CON/PRN/AUX/NUL/COM1–9/LPT1–9. Return a fresh filename/project/shot/version/extension dictionary, with a real integer version. This is a classroom grammar, not every production naming convention. No read, rename or write occurs. An explicit split-and-validate implementation is equally valid.
Refresh first: Text-kit setup, Names and canonical versions, String operations.
Trace a finished example
from text_tools.core import parse_filename
record = parse_filename("Night-Shift_take-2_v100.MOV")
print(record["project"], record["shot"], record["version"], record["extension"])
for name in ("Harbour_wide_v001.mp4", "Harbour_wide_v01.mp4\n"):
try:
parse_filename(name)
except ValueError:
print("refused")The first whole filename satisfies both shape and canonical numeric policy. The two near-matches fail: redundant leading zero and a trailing newline. The exception boundary handles expected input refusal; it does not swallow unexpected programming errors or mutate a file.
The finished implementation is in text_tools/core.py. Reading it is guided practice, not independent evidence.
Predict a near-match
Should Night_Shift_wide_v01.mp4 be split into a guessed project and shot?
Compare your answer · self-reviewed
No. The declared grammar gives exactly two underscore separators and forbids internal underscores. Refuse the ambiguity; a different production grammar needs a separately agreed contract.
Find the policy gap
Does matching two or three digits alone reject v001?
Compare your answer · self-reviewed
No. 001 matches the shape but is not the canonical spelling of integer 1. Compare the original digits with the two-digit-minimum format after range validation.
Recall ownership
Can changing the returned project modify the original string?
Compare your answer · self-reviewed
No. Strings are immutable and the result is a fresh dictionary. Still test repeated calls and preserve the original filename as supplied evidence.
Try the idea in this browser
Runs in this browser · optional preparation · local project checks remain separate
Try a small function before opening your local files. Python downloads when you choose Run; if it cannot load, your code stays here and the local kit still works. The worker executes on your device, not on a DVP server. Only run code you trust: this is not a hostile-code security sandbox.
JavaScript loads the practice controls. Python starts only after Run.
The tutor button only prepares a question locally. Review it and choose Send yourself; no code is sent merely by running or opening a lesson.
Output
Errors and check feedback
Read the browser task briefs without running Python
Guided parse a complete shot filename
Use invented in-memory text only. Reviewed bounded-input policies, fixed patterns and vocabulary are supplied and disclosed. Write your own parse_filename; the finished task function is not supplied. No file/export/provider operation runs and these checks are not local project or Portfolio II evidence. Implement parse_filename in practice.py with the full declared grammar, exact version policy, reserved components and fresh typed fields. You may disclose reuse of bounded_text or NAME_PATTERN. Implement your own task function; do not import the finished parser. A non-regex split/validate strategy must meet the same positive and negative cases.
Independent parse a complete shot filename
Use invented in-memory text only. Reviewed bounded-input policies, fixed patterns and vocabulary are supplied and disclosed. Write your own parse_filename; the finished task function is not supplied. No file/export/provider operation runs and these checks are not local project or Portfolio II evidence. Implement parse_filename in practice.py with the full declared grammar, exact version policy, reserved components and fresh typed fields. You may disclose reuse of bounded_text or NAME_PATTERN. Implement your own task function; do not import the finished parser. A non-regex split/validate strategy must meet the same positive and negative cases.
Change it, then build your own
One controlled change
Change the accepted name to project Museum, shot detail-2 and version 999. Then append a backup suffix, use an internal underscore and use v00. Predict the fields or refusal before running.
Your independent task
Implement parse_filename in practice.py with the full declared grammar, exact version policy, reserved components and fresh typed fields. You may disclose reuse of bounded_text or NAME_PATTERN. Implement your own task function; do not import the finished parser. A non-regex split/validate strategy must meet the same positive and negative cases.
What success looks like
The build1 tests accept changed case/hyphen names, versions and exact component boundaries; paths, suffixes, redundant zeros, Unicode digits, reserved components and invalid text are refused. A test pass does not mean media was inspected or renamed.
Hint 1 · a question
Write the exact five returned fields for one accepted name. Which separators are structural, and which characters belong inside a component?
Hint 2 · a concept cue
Separate whole-string shape, component constraints, numeric range and canonical spelling. A shape match alone is not all four checks.
Hint 3 · a localized example
For version 1, f"{1:02d}" is "01". Keep that comparison separate from the filename text you preserve in the returned record.
Need the complete worked solution?
Open text_tools/core.py from the kit. Trace it, close it, then try fresh inputs in your own files. Treat the attempt as guided; seeing the solution does not award a practical pass.
Course help is guidance, not independent evidence. With JavaScript, opening help records guidance locally; otherwise note it in your README. Reset does not erase that history.
Repair a failed check
If a suffix/newline passes, use fullmatch or explicit complete-string checks rather than substring search or strip. If v001 passes, add canonical text comparison. If underscores are guessed, validate exact field count. If bool or bytes are accepted as names, apply the input policy first.
NotImplementedError means a practice stub is still unfinished. Read the failing test name and the last error line. Change one behavior, rerun that build, then rerun all implemented builds.
Show it works on new inputs
Author six changed names: valid hyphenated project and version 999, invalid underscore ambiguity, newline, path and redundant-zero version. Record actual parsed fields/refusals, explain canonical text versus numeric value, and disclose any reviewed helpers.
Self-review: name the input, result, refused case and reason. Your local test output and explanation are separate from a quiz score; this page does not certify a pass.
Keep the idea
A useful parser follows an explicit grammar and refuses ambiguity. Matching text is not permission to modify a file.