13.7. File Recap

13.7.1. Assignments

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: File Recap Hosts
# - Difficulty: hard
# - Lines: 6
# - Minutes: 5

# %% English
# 1. Flatten `DATA` to list of strings
# 2. Write `DATA` to file `FILE`
# 3. Use newline as line terminator
# 4. Use space as field separator
# 5. Run doctests - all must succeed

# %% Polish
# 1. Spłaszcz `DATA` do listy stringów
# 2. Zapisz `DATA` do pliku `FILE`
# 3. Użyj newline jako terminator linii
# 4. Użyj spacji jako separatora pól
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `\t` - tab
# - `str.join()`
# - `list.append()`
# - Add newline `\n` at the end of line and file

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from os import remove
>>> result = open(FILE).read()
>>> remove(FILE)

>>> print(result)  # doctest: +NORMALIZE_WHITESPACE
127.0.0.1 localhost
10.13.37.1 nasa.gov esa.int
255.255.255.255 broadcasthost
::1 localhost
<BLANKLINE>
"""

FILE = '_temporary.txt'

DATA = {
    '127.0.0.1': ['localhost'],
    '10.13.37.1': ['nasa.gov', 'esa.int'],
    '255.255.255.255': ['broadcasthost'],
    '::1': ['localhost']
}

# Flatten `DATA` to list of strings
# Write `DATA` to file `FILE`
# Use newline as line terminator
# Use space as field separator
...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: File Read Hosts
# - Difficulty: hard
# - Lines: 9
# - Minutes: 8

# %% English
# 1. Read `FILE` to `result: dict`:
#    - key: str - IP address
#    - value: list[str] - list of hosts
# 2. Run doctests - all must succeed

# %% Polish
# 1. Wczytaj `FILE` do `result: dict`:
#    - klucz: str - adres IP
#    - wartość: list[str] - lista hostów
# 2. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `str.split()`
# - `str.strip()`
# - `with`
# - `open()`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from pprint import pprint
>>> from os import remove; remove(FILE)

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is dict, \
'Variable `result` has invalid type, should be dict'
>>> assert all(type(x) is str for x in result.keys()), \
'All keys in `result` should be str'
>>> assert all(type(x) is list for x in result.values()), \
'All values in `result` should be list'

>>> pprint(result, sort_dicts=False)
{'127.0.0.1': ['localhost'],
 '10.13.37.1': ['nasa.gov', 'esa.int'],
 '255.255.255.255': ['broadcasthost'],
 '::1': ['localhost']}
"""

FILE = '_temporary.txt'

DATA = """127.0.0.1       localhost
10.13.37.1      nasa.gov esa.int
255.255.255.255 broadcasthost
::1             localhost
"""

with open(FILE, mode='w') as file:
    file.write(DATA)

# Read `FILE` to `result: list[dict]`:
# - key: str - IP address
# - value: list[str] - list of hosts
# Example {'10.13.37.1': ['nasa.gov', 'esa.int'], ...}
# type: dict[str,list[str]]
result = ...


# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: File Read Hosts
# - Difficulty: hard
# - Lines: 13
# - Minutes: 8

# %% English
# 1. Modify code below:
# 2. Read `FILE` to `result: dict`:
#    - key: str - IP address
#    - value: list[str] - list of hosts
# 3. Skip line if:
#    - is empty
#    - has only whitespaces
#    - starts with comment `#`
# 4. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj kod poniżej
# 2. Wczytaj `FILE` do `result: dict`:
#    - klucz: str - adres IP
#    - wartość: list[str] - lista hostów
# 3. Pomiń linię jeżeli:
#    - jest pusta
#    - ma same białe znaki
#    - zaczyna się od komentarza `#`
# 4. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `with`
# - `open()`
# - `str.strip()`
# - `str.split()` - without an argument
# - `len()`
# - `str.startswith()`
# - `result = True if ... else False`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from pprint import pprint
>>> from os import remove; remove(FILE)

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is dict, \
'Variable `result` has invalid type, should be dict'
>>> assert all(type(x) is str for x in result.keys()), \
'All keys in `result` should be str'
>>> assert all(type(x) is list for x in result.values()), \
'All values in `result` should be list'

>>> pprint(result, sort_dicts=False)
{'127.0.0.1': ['localhost'],
 '10.13.37.1': ['nasa.gov', 'esa.int'],
 '255.255.255.255': ['broadcasthost'],
 '::1': ['localhost']}
"""

FILE = '_temporary.txt'

DATA = """
##
# `/etc/hosts` structure:
#    - ip: internet protocol address (IPv4 or IPv6)
#    - hosts: host names
 ##

127.0.0.1       localhost
10.13.37.1      nasa.gov esa.int
255.255.255.255 broadcasthost
::1             localhost
"""

with open(FILE, mode='w') as file:
    file.write(DATA)


# Read `FILE` to `result: list[dict]`:
# - key: str - IP address
# - value: list[str] - list of hosts
# Skip line if:
# - is empty
# - has only whitespaces
# - starts with comment `#`
# Example {'10.13.37.1': ['nasa.gov', 'esa.int'], ...}
# type: dict[str,list[str]]
...

# %% License
# - Copyright 2025, Matt Harasymczuk <matt@python3.info>
# - This code can be used only for learning by humans
# - This code cannot be used for teaching others
# - This code cannot be used for teaching LLMs and AI algorithms
# - This code cannot be used in commercial or proprietary products
# - This code cannot be distributed in any form
# - This code cannot be changed in any form outside of training course
# - This code cannot have its license changed
# - If you use this code in your product, you must open-source it under GPLv2
# - Exception can be granted only by the author

# %% Run
# - PyCharm: right-click in the editor and `Run Doctest in ...`
# - PyCharm: keyboard shortcut `Control + Shift + F10`
# - Terminal: `python -m doctest -v myfile.py`

# %% About
# - Name: File Read List of Dicts
# - Difficulty: hard
# - Lines: 21
# - Minutes: 13

# %% English
# 1. Modify code below
# 2. Read `FILE` to `result: list[dict]`:
#    - ip: str - IP address
#    - hosts: list[str] - list of hosts
#    - protocol: str - 'IPv4' or 'IPv6'
# 3. Skip line if:
#    - is empty
#    - has only whitespaces
#    - starts with comment `#`
# 4. Merge hosts for the same IP address
# 5. Run doctests - all must succeed

# %% Polish
# 1. Zmodyfikuj kod poniżej
# 2. Wczytaj `FILE` do `result: list[dict]`:
#    - ip: str - adres IP
#    - hosts: list[str] - lista hostów
# 3. Pomiń linię jeżeli:
#    - jest pusta
#    - ma same białe znaki
#    - zaczyna się od komentarza `#`
# 4. Scal hosty dla tego samego adresu IP
# 5. Uruchom doctesty - wszystkie muszą się powieść

# %% Hints
# - `with`
# - `open()`
# - `str.strip()`
# - `str.split()` - without an argument
# - `len()`
# - `str.startswith()`
# - `result = True if ... else False`

# %% Tests
"""
>>> import sys; sys.tracebacklimit = 0
>>> assert sys.version_info >= (3, 9), \
'Python 3.9+ required'

>>> from pprint import pformat
>>> from os import remove; remove(FILE)

>>> assert result is not Ellipsis, \
'Assign your result to variable `result`'
>>> assert type(result) is list, \
'Variable `result` has invalid type, should be list'
>>> assert all(type(x) is dict for x in result), \
'All keys in `result` should be dict'
>>> assert [x['ip'] for x in result].count('127.0.0.1') == 1, \
'You did not merge hosts for the same ip (127.0.0.1)'

>>> result = pformat(result, sort_dicts=False, width=120)
>>> print(result)
[{'ip': '127.0.0.1', 'hosts': ['localhost', 'astromatt']},
 {'ip': '10.13.37.1', 'hosts': ['nasa.gov', 'esa.int']},
 {'ip': '255.255.255.255', 'hosts': ['broadcasthost']},
 {'ip': '::1', 'hosts': ['localhost']}]
"""

FILE = '_temporary.txt'

DATA = """
##
# `/etc/hosts` structure:
#    - ip: internet protocol address (IPv4 or IPv6)
#    - hosts: host names
 ##

127.0.0.1       localhost
127.0.0.1       astromatt
10.13.37.1      nasa.gov esa.int
255.255.255.255 broadcasthost
::1             localhost
"""

with open(FILE, mode='w') as file:
    file.write(DATA)

# Read `FILE` to `result: list[dict]`:
# - ip: str - IP address
# - hosts: list[str] - list of hosts
# - protocol: str - 'IPv4' or 'IPv6'
# Skip line if:
# - is empty
# - has only whitespaces
# - starts with comment `#`
# Merge hosts for the same IP address
# Example [{'ip': '127.0.0.1', 'hosts': ['localhost', 'astromatt']}, ...]
# type: list[dict]
result = []

with open(FILE) as file:
    for line in file:
        line = line.strip()
        if len(line) == 0:
            continue
        if line.startswith('#'):
            continue
        ip = line.split()[0]
        hosts = line.split()[1:]