Compare commits

..

8 Commits

Author SHA1 Message Date
Luciano Ramalho
cf3161ca00 moved URL shortening code to https://github.com/pythonfluente/pythonfluente2e 2025-06-04 18:54:30 -03:00
Luciano Ramalho
8c8c08170a Merge pull request #57 from fluentpython/dependabot/pip/21-async/mojifinder/h11-0.16.0
build(deps): bump h11 from 0.12.0 to 0.16.0 in /21-async/mojifinder
2025-05-23 16:36:15 -03:00
Luciano Ramalho
162dbadbe5 links from vol1 to pythonfluente.com 2025-05-23 16:35:01 -03:00
Luciano Ramalho
cf99650007 minor refactoring to make it easier to call shorten() 2025-05-22 14:28:42 -03:00
Luciano Ramalho
ec03da74ca short.py appends timestamps to short.htaccesss 2025-05-22 13:44:46 -03:00
Luciano Ramalho
5b743b5bd7 short.py now reads files and stdin 2025-05-22 13:24:50 -03:00
Luciano Ramalho
648e9f6394 Update short.py to return list of URL substitutions 2025-05-22 10:05:54 -03:00
dependabot[bot]
c5490b1569 build(deps): bump h11 from 0.12.0 to 0.16.0 in /21-async/mojifinder
Bumps [h11](https://github.com/python-hyper/h11) from 0.12.0 to 0.16.0.
- [Commits](https://github.com/python-hyper/h11/compare/v0.12.0...v0.16.0)

---
updated-dependencies:
- dependency-name: h11
  dependency-version: 0.16.0
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-22 04:13:41 +00:00
8 changed files with 1098 additions and 1208 deletions

View File

@@ -1,6 +1,6 @@
click==7.1.2
fastapi==0.65.2
h11==0.12.0
h11==0.16.0
pydantic==1.10.13
starlette==0.40.0
typing-extensions==3.7.4.3

File diff suppressed because it is too large Load Diff

View File

@@ -1,52 +1,6 @@
# Short links for URLs in the book
This file is deployed as `.htaccess` to the FPY.LI domain
to map short URLs in Fluent Python to the original URLs.
## Problem: link rot
To update it, I use tools in this other repo:
_Fluent Python, Second Edition_ has more than 1000 links to external resources.
Inevitably, some of those links will rot as time passes.
But I can't change the URLs in the print book...
## Solution: indirection
I replaced almost all URLs in the book with shortened versions that go through the `fpy.li` site which I control.
The site has an `.htaccess` file with *temporary* redirects.
When I find out a link is stale, I can thange the redirect in `.htaccess` to a new target,
which may be a link to copy in the Internet Archive's
[Wayback Machine](https://archive.org/web/)
o the link in the book is back in service through the updated redirect.
## Help wanted
Please report broken links as bugs in the [`FPY.LI.htaccess`](FPY.LI.htaccess) file.
Also, feel free to send pull requests with fixes to that file.
When I accept a PR, I will redeploy it to `fpy.li/.htaccess`.
## Details
Almost all URLs in the book are replaced with shortened versions like
[`http://fpy.li/1-3`](http://fpy.li/1-3)—for chapter 1, link #3.
There are also custom short URLs like
[`https://fpy.li/code`](https://fpy.li/code) which redirects to the example code repository.
I used custom short URLs for URLs with 3 or more mentions, or links to PEPs.
Exceptions:
- URLs with `oreilly` in them are unchanged;
- `fluentpython.com` URL (with no path) is unchanged;
The `custom.htaccess` file contains redirects with custom names
plus numbered URLs generated from the links in each chapter in
the Second Edition in English.
`short.htaccess` has redirects made by `short.py`, starting
with the Second Edition in Brazilian Portuguese.
```shell
cat custom.htaccess short.htaccess > FPY.LI.htaccess
```
`FPY.LI.htaccess` is deployed at the root folder in `http://fpy.li`.
https://github.com/pythonfluente/pythonfluente2e

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +0,0 @@
#!/bin/bash
scp FPY.LI.htaccess dh_i4p2ka@fpy.li:~/fpy.li/.htaccess

View File

@@ -1 +0,0 @@
# file created and managed by short.py

View File

@@ -1,80 +0,0 @@
#!/usr/bin/env python3
import itertools
from collections.abc import Iterator
def load_redirects():
redirects = {}
targets = {}
for filename in ('custom.htaccess', 'short.htaccess'):
with open(filename) as fp:
for line in fp:
if line.startswith('RedirectTemp'):
_, short, long = line.split()
short = short[1:] # Remove leading slash
assert short not in redirects, f"{filename}: duplicate redirect from {short}"
# custom is live since 2022, we cannot change it remove duplicate targets
if not filename.startswith('custom'):
assert long not in targets, f"{filename}: Duplicate redirect to {long}"
redirects[short] = long
targets[long] = short
return redirects, targets
SDIGITS = '23456789abcdefghjkmnpqrstvwxyz'
def gen_short() -> Iterator[str]:
"""
Generate every possible sequence of SDIGITS.
"""
length = 1
while True:
for short in itertools.product(SDIGITS, repeat=length):
yield ''.join(short)
length += 1
def shorten(n: int) -> str:
"""
Get Nth short URL made from SDIGITS, where 0 is the first.
"""
iter_short = gen_short()
for _ in range(n+1):
short = next(iter_short)
return short
def gen_free_short(redirects: dict) -> Iterator[str]:
"""
Generate next available short URL.
"""
for short in gen_short():
if short not in redirects:
yield short
def new_urls(urls: list[str], redirects: dict, targets: dict) -> None:
iter_short = gen_free_short(redirects)
with open('short.htaccess', 'a') as fp:
for url in urls:
assert 'fpy.li' not in url, f"{url} is a fpy.li URL"
if url in targets:
continue
short = next(iter_short)
redirects[short] = url
targets[url] = short
fp.write(f"RedirectTemp /{short} {url}\n")
def main():
from random import randrange
urls = [f'https://example.com/{randrange(100000)}.html' for n in range(7)]
redirects, targets = load_redirects()
new_urls(urls, redirects, targets)
if __name__ == '__main__':
main()

4
ruff.toml Normal file
View File

@@ -0,0 +1,4 @@
line-length = 100
[format]
# Like Python's repr(), use single quotes for strings.
quote-style = "single"