1
0
mirror of https://github.com/jbranchaud/til synced 2026-07-02 23:58:25 +00:00

Compare commits

...

3 Commits

Author SHA1 Message Date
jbranchaud a8c35e2458 Add Define Sequence Of Tests With Parametrize Decorator as a Python TIL 2026-05-01 21:30:20 -05:00
jbranchaud c0ad3cee4d Add Assert Is Only A Development Check as a Python TIL 2026-05-01 17:01:14 -05:00
jbranchaud 9bd1bb413a Add Reverse Each Line Of A File as a Unix TIL 2026-05-01 16:07:15 -05:00
4 changed files with 166 additions and 1 deletions
+4 -1
View File
@@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1780 TILs and counting..._
_1783 TILs and counting..._
See some of the other learning resources I work on:
@@ -1050,6 +1050,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Access Instance Variables](python/access-instance-variables.md)
- [Access Most Recent Return Value In REPL](python/access-most-recent-return-value-in-repl.md)
- [Access Variables Outside Loop Scope](python/access-variables-outside-loop-scope.md)
- [Assert Is Only A Development Check](python/assert-is-only-a-development-check.md)
- [Avoid Modification With Frozen Dataclass](python/avoid-modification-with-frozen-dataclass.md)
- [Break Debugger On First Line Of Program](python/break-debugger-on-first-line-of-program.md)
- [Check If Package Is Installed With Pip](python/check-if-package-is-installed-with-pip.md)
@@ -1057,6 +1058,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Create A Dummy DataFrame In Pandas](python/create-a-dummy-dataframe-in-pandas.md)
- [Create A Range Of Descending Values](python/create-a-range-of-descending-values.md)
- [Deduplicate A List Into A Tuple](python/deduplicate-a-list-into-a-tuple.md)
- [Define Sequence Of Tests With Parametrize Decorator](python/define-sequence-of-tests-with-parametrize-decorator.md)
- [Dunder Methods](python/dunder-methods.md)
- [Easy Key-Value Aggregates With defaultdict](python/easy-key-value-aggregates-with-defaultdict.md)
- [Install With PIP For Specific Interpreter](python/install-with-pip-for-specific-interpreter.md)
@@ -1796,6 +1798,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md)
- [Repeat Yourself](unix/repeat-yourself.md)
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
- [Reverse Each Line Of A File](unix/reverse-each-line-of-a-file.md)
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)
- [Run A cURL Command Without The Progress Meter](unix/run-a-curl-command-without-the-progress-meter.md)
- [Safely Edit The Sudoers File With Vim](unix/safely-edit-the-sudoers-file-with-vim.md)
@@ -0,0 +1,38 @@
# Assert Is Only A Development Check
The `assert` keyword is used in Python to write a statement that will check some
assertion and raise an error if it isn't met. This is only meant to be used as a
check during development because it can be easily optimized out of the code.
```python
stuff = None
assert stuff, "We need to have some stuff to proceed"
print(f"We have {stuff or 'something'}!")
```
If I execute this code with `python`, it will raise on that second line of code.
```bash
python assert_example.py
Traceback (most recent call last):
File "/Users/lastword/dev/jbranchaud/py-vmt/assert_example.py", line 3, in <module>
assert stuff, "We need to have some stuff to proceed"
^^^^^
AssertionError: We need to have some stuff to proceed
```
This `assert` statement will be stripped out of the compiled bytecode if the
`-O` (capital o) flag is used. Notice how running the same file with that flag
does not lead to an `AssertionError`.
```python
python -O assert_example.py
We have something!
```
If I want to make sanity checks for situations that would be caused by a bug in
the code, an `assert` statement can be a good candidate. However, if I am making
runtime checks like validating user input, then an `if` statement and raising
something like a `ValueError` is better.
@@ -0,0 +1,60 @@
# Define Sequence Of Tests With Parametrize Decorator
I have a function that I want to test across a bunch of different inputs. That
way I can make sure the logic of that function handles all the different
scenarios I have in mind.
While working on [`py-vmt`](https://github.com/jbranchaud/py-vmt), I started by
writing a big single test function with a sequence of variable assignments and
`assert` statements. Here's my starting point:
```python
def test_format_time_delta_everything():
# less than a minute
thirty_seconds = timedelta(seconds=30)
assert "30s" == format_time_delta(thirty_seconds)
# one minute exactly
one_minute = timedelta(seconds=60)
assert "1m" == format_time_delta(one_minute)
# more than a minute
assert "1m30s" == format_time_delta(one_minute + thirty_seconds)
# bunch of minutes and seconds
delta = timedelta(minutes=24, seconds=8)
assert "24m8s" == format_time_delta(delta)
# one hour exactly
one_hour = timedelta(hours=1)
assert "1h" == format_time_delta(one_hour)
# more than one hour
assert "1h24m" == format_time_delta(one_hour + delta)
```
I knew I would eventually need to break it up into individual test functions,
but I couldn't bare to start there because it seemed quite repetitive.
There is another way to approach this without all the duplication. Pytest comes
with [a "parametrize" decorator](https://docs.pytest.org/en/stable/example/parametrize.html). This is
used to define a set of test data (and expected values) that will get passed
one-by-one to the test function as parameters.
```python
@pytest.mark.parametrize("input,expected", [
(timedelta(seconds=30), "30s"),
(timedelta(seconds=60), "1m"),
(timedelta(seconds=90), "1m30s"),
(timedelta(minutes=24, seconds=8), "24m8s"),
(timedelta(hours=1), "1h"),
(timedelta(hours=1, minutes=24, seconds=8), "1h24m"),
])
def test_format_time_delta(input, expected):
assert format_time_delta(input) == expected
```
I ditch all of the duplication this way. I define a list of tuples that
represent my input values and expected values. Then the body of the test can be
minimal. And I get a separate test execution for each parameter tuple making it
easier to see fine-grained pass/fail results.
+64
View File
@@ -0,0 +1,64 @@
# Reverse Each Line Of A File
The [`rev` command](https://man7.org/linux/man-pages/man1/rev.1.html) can be
used to reverse each line in a file. Every line is left where it is relative to
other lines, but the contents of each line is reversed.
So a file that contains the following text:
```bash
cat stuff.md
Three
Two
One
go racecar go
```
can be piped to `rev` to get the following output:
```bash
rev stuff.md
eerhT
owT
enO
og racecar og
```
This is an odd utility that doesn't have too much use that I can imagine. After
a brief chat with Claude where I asked for some practical use cases, the one
that stood out the most to me is to reverse a list of filenames, sort them, and
then reverse them again (putting them back in readable order). This can shuffle
filenames with similar endings near each other like source and test files.
Here is a list of files for me [`py-vmt`
project](https://github.com/jbranchaud/py-vmt):
```bash
fd -t f .
README.md
pyproject.toml
src/py_vmt/__init__.py
src/py_vmt/cli.py
src/py_vmt/session.py
src/py_vmt/time_helpers.py
tests/src/py_vmt/test_cli.py
tests/src/py_vmt/test_session.py
```
Now I can pipe the output of that `fd` command through `rev | sort | rev` to get
my files organized in a different way.
```bash
fd -t f . | rev | sort | rev
README.md
pyproject.toml
src/py_vmt/__init__.py
tests/src/py_vmt/test_cli.py
src/py_vmt/cli.py
tests/src/py_vmt/test_session.py
src/py_vmt/session.py
src/py_vmt/time_helpers.py
```
Again the value of doing something like this is a bit tenuous. At the very least
it is fun to know about.