sync with Atlas
This commit is contained in:
@@ -23,8 +23,8 @@ metro_areas = [
|
|||||||
def main():
|
def main():
|
||||||
print(f'{"":15} | {"latitude":>9} | {"longitude":>9}')
|
print(f'{"":15} | {"latitude":>9} | {"longitude":>9}')
|
||||||
for record in metro_areas:
|
for record in metro_areas:
|
||||||
match record:
|
match record: # <1>
|
||||||
case [name, _, _, (lat, lon)] if lon <= 0:
|
case [name, _, _, (lat, lon)] if lon <= 0: # <2>
|
||||||
print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')
|
print(f'{name:15} | {lat:9.4f} | {lon:9.4f}')
|
||||||
# end::MAIN[]
|
# end::MAIN[]
|
||||||
|
|
||||||
|
|||||||
72
04-text-byte/charfinder/README.rst
Normal file
72
04-text-byte/charfinder/README.rst
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
========================
|
||||||
|
Character Finder Utility
|
||||||
|
========================
|
||||||
|
|
||||||
|
Usage tips
|
||||||
|
==========
|
||||||
|
|
||||||
|
`cf.py` works as an executable on Unix-like systems,
|
||||||
|
if you have `python3` on your `$PATH`::
|
||||||
|
|
||||||
|
$ chmod +x cf.py
|
||||||
|
$ ./cf.py cat eyes
|
||||||
|
U+1F638 😸 GRINNING CAT FACE WITH SMILING EYES
|
||||||
|
U+1F63B 😻 SMILING CAT FACE WITH HEART-SHAPED EYES
|
||||||
|
U+1F63D 😽 KISSING CAT FACE WITH CLOSED EYES
|
||||||
|
|
||||||
|
Use `wc -l` to count the number of hits::
|
||||||
|
|
||||||
|
$ ./cf.py hieroglyph | wc -l
|
||||||
|
1663
|
||||||
|
|
||||||
|
With `tee` you can get the output and the count::
|
||||||
|
|
||||||
|
$ ./cf.py trigram | tee >(wc -l)
|
||||||
|
U+2630 ☰ TRIGRAM FOR HEAVEN
|
||||||
|
U+2631 ☱ TRIGRAM FOR LAKE
|
||||||
|
U+2632 ☲ TRIGRAM FOR FIRE
|
||||||
|
U+2633 ☳ TRIGRAM FOR THUNDER
|
||||||
|
U+2634 ☴ TRIGRAM FOR WIND
|
||||||
|
U+2635 ☵ TRIGRAM FOR WATER
|
||||||
|
U+2636 ☶ TRIGRAM FOR MOUNTAIN
|
||||||
|
U+2637 ☷ TRIGRAM FOR EARTH
|
||||||
|
8
|
||||||
|
|
||||||
|
|
||||||
|
Running the tests
|
||||||
|
=================
|
||||||
|
|
||||||
|
Run the ``doctest`` module from the command line on
|
||||||
|
this README.rst file (using ``-v`` to make tests visible)::
|
||||||
|
|
||||||
|
$ python3 -m doctest README.rst -v
|
||||||
|
|
||||||
|
That's what the ``test.sh`` script does.
|
||||||
|
|
||||||
|
|
||||||
|
Tests
|
||||||
|
-----
|
||||||
|
|
||||||
|
Import functions for testing::
|
||||||
|
|
||||||
|
>>> from cf import find, main
|
||||||
|
|
||||||
|
Test ``find`` with single result::
|
||||||
|
|
||||||
|
>>> find('sign', 'registered') # doctest:+NORMALIZE_WHITESPACE
|
||||||
|
U+00AE ® REGISTERED SIGN
|
||||||
|
|
||||||
|
Test ``find`` with two results::
|
||||||
|
|
||||||
|
>>> find('chess', 'queen', last=0xFFFF) # doctest:+NORMALIZE_WHITESPACE
|
||||||
|
U+2655 ♕ WHITE CHESS QUEEN
|
||||||
|
U+265B ♛ BLACK CHESS QUEEN
|
||||||
|
|
||||||
|
Test ``find`` with no results::
|
||||||
|
|
||||||
|
>>> find('no_such_character')
|
||||||
|
|
||||||
|
Test ``main`` with no words::
|
||||||
|
|
||||||
|
>>> main([])
|
||||||
|
Please provide words to find.
|
||||||
@@ -4,18 +4,13 @@ import unicodedata
|
|||||||
|
|
||||||
FIRST, LAST = ord(' '), sys.maxunicode # <1>
|
FIRST, LAST = ord(' '), sys.maxunicode # <1>
|
||||||
|
|
||||||
|
|
||||||
def find(*query_words, first=FIRST, last=LAST): # <2>
|
def find(*query_words, first=FIRST, last=LAST): # <2>
|
||||||
query = {w.upper() for w in query_words} # <3>
|
query = {w.upper() for w in query_words} # <3>
|
||||||
count = 0
|
|
||||||
for code in range(first, last + 1):
|
for code in range(first, last + 1):
|
||||||
char = chr(code) # <4>
|
char = chr(code) # <4>
|
||||||
name = unicodedata.name(char, None) # <5>
|
name = unicodedata.name(char, None) # <5>
|
||||||
if name and query.issubset(name.split()): # <6>
|
if name and query.issubset(name.split()): # <6>
|
||||||
print(f'U+{code:04X}\t{char}\t{name}') # <7>
|
print(f'U+{code:04X}\t{char}\t{name}') # <7>
|
||||||
count += 1
|
|
||||||
print(f'({count} found)')
|
|
||||||
|
|
||||||
|
|
||||||
def main(words):
|
def main(words):
|
||||||
if words:
|
if words:
|
||||||
@@ -23,6 +18,5 @@ def main(words):
|
|||||||
else:
|
else:
|
||||||
print('Please provide words to find.')
|
print('Please provide words to find.')
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv[1:])
|
main(sys.argv[1:])
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
Doctests for ``cf.py``
|
|
||||||
======================
|
|
||||||
|
|
||||||
How to run the tests
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Run the ``doctest`` module from the command line::
|
|
||||||
|
|
||||||
$ python3 -m doctest cf_tests.rst
|
|
||||||
|
|
||||||
|
|
||||||
Tests
|
|
||||||
-----
|
|
||||||
|
|
||||||
Import functions for testing::
|
|
||||||
|
|
||||||
>>> from cf import find, main
|
|
||||||
|
|
||||||
Test ``find`` with single result::
|
|
||||||
|
|
||||||
>>> find("sign", "registered") # doctest:+NORMALIZE_WHITESPACE
|
|
||||||
U+00AE ® REGISTERED SIGN
|
|
||||||
(1 found)
|
|
||||||
|
|
||||||
|
|
||||||
Test ``find`` with two results::
|
|
||||||
|
|
||||||
>>> find("chess", "queen", last=0xFFFF) # doctest:+NORMALIZE_WHITESPACE
|
|
||||||
U+2655 ♕ WHITE CHESS QUEEN
|
|
||||||
U+265B ♛ BLACK CHESS QUEEN
|
|
||||||
(2 found)
|
|
||||||
|
|
||||||
Test ``main`` with no words::
|
|
||||||
|
|
||||||
>>> main([])
|
|
||||||
Please provide words to find.
|
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
python3 -m doctest cf_tests.rst $1
|
python3 -m doctest README.rst $1
|
||||||
|
|||||||
@@ -1,40 +1,48 @@
|
|||||||
"""
|
"""
|
||||||
>>> clip('banana ', 6)
|
>>> clip('banana split', 5)
|
||||||
'banana'
|
|
||||||
>>> clip('banana ', 7)
|
|
||||||
'banana'
|
|
||||||
>>> clip('banana ', 5)
|
|
||||||
'banana'
|
'banana'
|
||||||
>>> clip('banana split', 6)
|
>>> clip('banana split', 6)
|
||||||
'banana'
|
'banana'
|
||||||
>>> clip('banana split', 7)
|
>>> clip('banana split', 7)
|
||||||
'banana'
|
'banana'
|
||||||
>>> clip('banana split', 10)
|
>>> clip('banana split', 8)
|
||||||
'banana'
|
'banana'
|
||||||
>>> clip('banana split', 11)
|
>>> clip('banana split', 11)
|
||||||
'banana'
|
'banana'
|
||||||
>>> clip('banana split', 12)
|
>>> clip('banana split', 12)
|
||||||
'banana split'
|
'banana split'
|
||||||
>>> clip('bananasplit', 5)
|
>>> clip('banana-split', 3)
|
||||||
'bananasplit'
|
'banana-split'
|
||||||
>>> clip('banana split', 8)
|
|
||||||
'banana'
|
Jess' tests:
|
||||||
|
|
||||||
|
>>> text = 'The quick brown fox jumps over the lazy dog.'
|
||||||
|
>>> clip14 = clip(text, max_len=14)
|
||||||
|
>>> clip14
|
||||||
|
'The quick'
|
||||||
|
>>> len(clip14)
|
||||||
|
9
|
||||||
|
>>> clip15 = clip(text, max_len=15)
|
||||||
|
>>> clip15
|
||||||
|
'The quick brown'
|
||||||
|
>>> len(clip15)
|
||||||
|
15
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# tag::CLIP[]
|
# tag::CLIP[]
|
||||||
def clip(text, max_len=80):
|
def clip(text, max_len=80):
|
||||||
"""Return text clipped at the last space before or after max_len"""
|
"""Return max_len characters clipped at space if possible"""
|
||||||
text = text.rstrip()
|
text = text.rstrip()
|
||||||
end = len(text)
|
if len(text) <= max_len or ' ' not in text:
|
||||||
if end <= max_len:
|
|
||||||
return text
|
return text
|
||||||
space_before = text.rfind(' ', 0, max_len)
|
end = len(text)
|
||||||
if space_before >= 0:
|
space_at = text.rfind(' ', 0, max_len + 1)
|
||||||
end = space_before
|
if space_at >= 0:
|
||||||
|
end = space_at
|
||||||
else:
|
else:
|
||||||
space_after = text.find(' ', max_len)
|
space_at = text.find(' ', max_len)
|
||||||
if space_after >= 0:
|
if space_at >= 0:
|
||||||
end = space_after
|
end = space_at
|
||||||
return text[:end].rstrip()
|
return text[:end].rstrip()
|
||||||
# end::CLIP[]
|
# end::CLIP[]
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,6 @@
|
|||||||
>>> clip.__code__ # doctest: +ELLIPSIS
|
>>> clip.__code__ # doctest: +ELLIPSIS
|
||||||
<code object clip at 0x...>
|
<code object clip at 0x...>
|
||||||
>>> clip.__code__.co_varnames
|
>>> clip.__code__.co_varnames
|
||||||
('text', 'max_len', 'end', 'space_before', 'space_after')
|
('text', 'max_len', 'end', 'space_at')
|
||||||
>>> clip.__code__.co_argcount
|
>>> clip.__code__.co_argcount
|
||||||
2
|
2
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
# tag::BOOKDICT[]
|
# tag::BOOKDICT[]
|
||||||
from typing import TypedDict, List
|
from typing import TypedDict
|
||||||
import json
|
import json
|
||||||
|
|
||||||
class BookDict(TypedDict):
|
class BookDict(TypedDict):
|
||||||
isbn: str
|
isbn: str
|
||||||
title: str
|
title: str
|
||||||
authors: List[str]
|
authors: list[str]
|
||||||
pagecount: int
|
pagecount: int
|
||||||
# end::BOOKDICT[]
|
# end::BOOKDICT[]
|
||||||
|
|
||||||
@@ -13,7 +13,7 @@ class BookDict(TypedDict):
|
|||||||
AUTHOR_EL = '<AUTHOR>{}</AUTHOR>'
|
AUTHOR_EL = '<AUTHOR>{}</AUTHOR>'
|
||||||
|
|
||||||
def to_xml(book: BookDict) -> str: # <1>
|
def to_xml(book: BookDict) -> str: # <1>
|
||||||
elements: List[str] = [] # <2>
|
elements: list[str] = [] # <2>
|
||||||
for key, value in book.items():
|
for key, value in book.items():
|
||||||
if isinstance(value, list): # <3>
|
if isinstance(value, list): # <3>
|
||||||
elements.extend(
|
elements.extend(
|
||||||
@@ -29,4 +29,4 @@ def to_xml(book: BookDict) -> str: # <1>
|
|||||||
def from_json(data: str) -> BookDict:
|
def from_json(data: str) -> BookDict:
|
||||||
whatever: BookDict = json.loads(data) # <1>
|
whatever: BookDict = json.loads(data) # <1>
|
||||||
return whatever # <2>
|
return whatever # <2>
|
||||||
# end::FROMJSON[]
|
# end::FROMJSON[]
|
||||||
|
|||||||
Reference in New Issue
Block a user