<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xml" href="/cjs/screen.xsl" media="screen"?>
<lecture>

<meta>
  <maintitle>Python</maintitle>
  <author>Jiří Znamenáček</author>
  <title>„pathlib“ – objektový přístup k cestě</title>
  <date>2018-03-10</date>
  <link><!--a href="http://vyuka.ookami.cz" rel="external">http://vyuka.ookami.cz</a--></link>
  <!--
  <style>
    /* CSSka */
  </style>
  <use-math/>
  -->
</meta>
<!--
  „“–…
  ←→ ↑↓ ↔↕
  ↵ aneb &#x21B5; aneb \r aneb CR aneb CarriageReturn
-->
<!--

dir( Path() )
[
    '__bytes__', '__class__', '__delattr__', '__dir__', '__doc__', '__enter__',
    '__eq__', '__exit__', '__format__', '__fspath__', '__ge__', '__getattribute__',
    '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__',
    '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__',
    '__rtruediv__', '__setattr__', '__sizeof__', '__slots__', '__str__',
    '__subclasshook__', '__truediv__',

    '_accessor', '_cached_cparts', '_closed', '_cparts', '_drv', '_flavour',
    '_format_parsed_parts', '_from_parsed_parts', '_from_parts', '_hash', '_init',
    '_make_child', '_make_child_relpath', '_opener', '_parse_args', '_parts',
    '_pparts', '_raise_closed', '_raw_open', '_root', '_str',

    'absolute', 'anchor', 'as_posix', 'as_uri',
    'chmod', 'cwd',
    'drive',
    'exists', 'expanduser',
    'glob', 'group',
    'home',
    'is_absolute', 'is_block_device', 'is_char_device', 'is_dir', 'is_fifo', 'is_file', 'is_reserved', 'is_socket', 'is_symlink',
    'iterdir',
    'joinpath',
    'lchmod', 'lstat',
    'match', 'mkdir',
    'name',
    'open', 'owner',
    'parent', 'parents', 'parts',
    'read_bytes', 'read_text', 'relative_to', 'rename', 'replace', 'resolve', 'rglob', 'rmdir', 'root',
    'samefile', 'stat', 'stem', 'suffix', 'suffixes', 'symlink_to',
    'touch',
    'unlink',
    'with_name', 'with_suffix', 'write_bytes', 'write_text'
]

>>> from pathlib import PurePath
>>> dir( PurePath() )
[
    '__bytes__', '__class__', '__delattr__', '__dir__', '__doc__', '__eq__',
    '__format__', '__fspath__', '__ge__', '__getattribute__', '__gt__', '__hash__',
    '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__',
    '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rtruediv__',
    '__setattr__', '__sizeof__', '__slots__', '__str__', '__subclasshook__',
    '__truediv__',
    
    '_cached_cparts', '_cparts', '_drv', '_flavour', '_format_parsed_parts',
    '_from_parsed_parts', '_from_parts', '_hash', '_init', '_make_child',
    '_parse_args', '_parts', '_pparts', '_root', '_str',

    'anchor', 'as_posix', 'as_uri', 'drive', 'is_absolute', 'is_reserved',
    'joinpath', 'match', 'name', 'parent', 'parents', 'parts', 'relative_to',
    'root', 'stem', 'suffix', 'suffixes', 'with_name', 'with_suffix'
]

>>> p = set(dir(Path()))
>>> pp = set(dir(PurePath()))
>>> p - pp
{'read_text', 'chmod', 'is_socket', 'is_symlink', 'expanduser', 'rglob', 'touch', 'resolve', '__exit__', 'lstat', '_raise_closed', 'cwd', '_opener', 'rmdir', 'is_dir', 'is_fifo', 'mkdir', 'glob', 'iterdir', 'absolute', '_raw_open', 'rename', 'is_file', 'replace', 'open', '_make_child_relpath', '_accessor', 'samefile', 'lchmod', 'write_bytes', 'is_block_device', 'unlink', 'is_char_device', 'exists', 'read_bytes', 'group', 'stat', 'write_text', '__enter__', '_closed', 'owner', 'symlink_to', 'home'}
>>> sorted(p - pp)
[
    '__enter__', '__exit__',
    
    '_accessor', '_closed', '_make_child_relpath', '_opener', '_raise_closed', '_raw_open',

    'absolute', 'chmod', 'cwd', 'exists', 'expanduser', 'glob', 'group', 'home',
    'is_block_device', 'is_char_device', 'is_dir', 'is_fifo', 'is_file', 'is_socket', 'is_symlink',
    'iterdir', 'lchmod', 'lstat', 'mkdir', 'open', 'owner', 'read_bytes', 'read_text',
    'rename', 'replace', 'resolve', 'rglob', 'rmdir', 'samefile', 'stat', 'symlink_to',
    'touch', 'unlink', 'write_bytes', 'write_text'
]

-->


<slide title="Úvod">

    <p>
        Přístup k souborovému systému na základě řetězcových cest k souborům a adresářům (typicky pomocí modulů <a href="/materialy/python/modules/os.xml"><code>os.path</code></a> a <code>glob.glob</code>) je značně nespolehlivý<sup>*</sup>. Proto se v Python'u 3.4 objevil úplně nový a značně abstrahovaný objektový přístup k cestám postavený na modulu <code>pathlib</code> (a dalších jemu podobných starších návrzích).
    </p>
    <note>
        <sup>*</sup> Obzvláště Linux se svým přístupem „název souboru je (téměř) libovolná sekvence bajtů“ v tom dělá pěkný zmatek.
    </note>
    <p>
        Z hlediska přístupu k souborovému systému je tohle rozhodně ta správnější cesta do budoucnosti, protože jinak se dříve nebo později potkáte s velmi těžko řešitelným strašidlem kódování řetězců.
    </p>
    <note>
        Jak GvR při jeho přijetí <a href="https://mail.python.org/pipermail/python-dev/2013-November/130424.html" class="external">poznamenal</a>: "pathlib  won't replace os.path immediately …, but in the long term I expect os.path to die a quiet death"
    </note>

</slide>
<slide title="Ochutnávka">

    <p>
        Než se na modul <em>pathlib</em> podíváme pořádně, ukažme si pár jednoduchých příkladů. Nejdříve základní manipulaci s cestou:
    </p>
    <example lang="python">
        >>> from pathlib import Path

        # bez udání cesty jsme v akutálním adresáři '.'
        >>> p0 = Path()
        >>> p0
        PosixPath('.')
        >>> p0.resolve()
        PosixPath('/tmp')

        # cesty se (ne)překvapivě spojují lomítky
        >>> p1 = p0 / 'cesta'
        >>> p1
        PosixPath('cesta')
        >>> p1.resolve()
        PosixPath('/tmp/cesta')

        # přičemž stačí, aby typu „Path“ byl pouze pravý operand
        >>> p2 = '..' / p0
        >>> p2
        PosixPath('..')
        >>> p2.resolve()
        PosixPath('/')

        # k dispozici máme užitečné funkce
        >>> Path().home()
        PosixPath('/home/pirat')
    </example>
    <p>
        A teď ještě pár operací s odkazem na soubor:
    </p>
    <example lang="python">
        # soubor má nějaké vlastnosti
        >>> p = Path().cwd() / 'pokus.txt'
        >>> p
        PosixPath('/tmp/pokus.txt')
        >>> p.parts
        ('/', 'tmp', 'pokus.txt')
        >>> p.parent
        PosixPath('/tmp')

        # souboru přímo vidíme na „vnitřnosti“ (od Python'u 3.5)..
        >>> p.write_text('příliš žluťoučký kůň', encoding='utf-8')
        20
        >>> p.read_bytes()
        b'p\xc5\x99\xc3\xadli\xc5\xa1 \xc5\xbelu\xc5\xa5ou\xc4\x8dk\xc3\xbd k\xc5\xaf\xc5\x88'

        # ..a můžeme s ním i celkem libovolně nakládat
        >>> t = Path('tmp.txt')
        >>> t
        PosixPath('tmp.txt')
        >>> t.resolve()
        PosixPath('/tmp/tmp.txt')
        >>> p.rename(t)
        >>> p
        PosixPath('/tmp/pokus.txt')
        >>> p.exists()
        False
        >>> t
        PosixPath('tmp.txt')
        >>> t.exists()
        True
        >>> t.read_text(encoding='utf-8')
        'příliš žluťoučký kůň'
    </example>
    <p>
        Ne všechny operace jsou asi úplně průhledné, ale když nic jiného, tak se všechny odehrávají na jednom jediném objektu a ne pomocí armády funkcí v modulu <em>os</em>.
    </p>
    <note>
        I když spousta metod objektu <em>Path</em> je (zatím) interně implementována právě voláním odpovídajících funkcí v modulu <em>os</em> ^_~
    </note>

</slide>
<slide title="Celkový přehled">

    <p>
        Z úplně nejvyššího pohledu se modul <strong><em>pathlib</em></strong> skládá z hierarchie tříd, které postupně implementují..
    </p>
    <ul>
        <li>
            abstraktní operace s cestami bez vlastního vstupu/výstupu na souborový systém (tzv. <em>Pure</em>-cesty);
        </li>
        <li>
            konkrétní operace s objekty na souborovém systému;
        </li>
    </ul>
    <p>
        ..a to obé jak ve variantách pro operační systém Windows (<em>Windows</em>) a Linux (<em>Posix</em>), tak především jednotným způsobem pro všechny podporované operační systémy:
    </p>
    <img src="_files/pathlib-inheritance.png" width="538" height="495" class="center" alt="hierarchie tříd modulu „pathlib“" />
    <p class="copyright">
        <a href="https://docs.python.org/3/copyright.html" class="external">© Copyright 2001-2018, Python Software Foundation.</a>
    </p>
    <p>
        Ve většině případů budete v reálu asi pracovat s jednou jedinou třídou, a to <strong><code>Path</code></strong>, která pro vás abstrahuje operační systém, na kterém spouštíte skript, abyste se o něj nemuseli starat.
    </p>

</slide>
<slide title="A) Abstraktní „Pure“-cesty">

    <p>
        <strong><em>Abstraktní cesty</em> nemají přístup na souborový systém</strong>, takže si operacemi na nich nic náhodou nesmažete, nicméně i tak poskytují armádu metod a vlastností užitečných pro manipulaci s cestami.
    </p>
    <note>
        PS: Základní třídou je <em>pathlib.PurePath</em>, která se podle aktuálního operačního systému realizuje jako <em>PurePosixPath</em> nebo <em>PureWindowsPath</em>. Obě konkrétnější třídy si můžete libovolně zavolat též, pokud z nějakého důvodu potřebujete na jedné platformě manipulovat s cestami platformy druhé.
    </note>

</slide>
<slide title="Vytvoření cesty">

    <p>
        Cesta se dá vytvořit mnoha způsoby:
    </p>
    <ul>
        <li>
            přímým zadáním
            <example lang="python">
                >>> p = PurePath('/tmp/pokus/tmp.tar.gz')
                >>> p
                PurePosixPath('/tmp/pokus/tmp.tar.gz')
            </example>
        </li>
        <li>
            pomocí operátoru <code>/</code>, například
            <example lang="python">
                >>> p = PurePath('/tmp') / 'pokus' / PurePath('tmp.tar.gz')
                >>> p
                PurePosixPath('/tmp/pokus/tmp.tar.gz')

                # Ale pozor!
                >>> p = PurePath('/tmp') / '/pokus' / PurePath('tmp.tar.gz')
                >>> p
                PurePosixPath('/pokus/tmp.tar.gz')
            </example>
        </li>
        <li>
            vypsáním (libovolných) jednotlivých částí
            <example lang="python">
                >>> p = PurePath('/tmp', 'pokus/tmp.tar.gz')
                >>> p
                PurePosixPath('/tmp/pokus/tmp.tar.gz')
            </example>
        </li>
        <li>
            složením částí pomocí dedikované funkce
            <example lang="python">
                # jako řetězce
                >>> p = PurePath('/tmp').joinpath('pokus', 'tmp.tar.gz')
                >>> p
                PurePosixPath('/tmp/pokus/tmp.tar.gz')

                # ale i jako objekty „pathlib“
                >>> p = PurePath('/tmp').joinpath('pokus', PurePath('tmp.tar.gz'))
                >>> p
                PurePosixPath('/tmp/pokus/tmp.tar.gz')
            </example>
        </li>
    </ul>

</slide>
<slide title="Informace o složkách cesty">

    <p>
        Při zadání <code>p = PurePath('/tmp/pokus/tmp.tar.gz')</code> (ale může se pochopitelně jednat i o adresář!) dostaneme:
    </p>
    <ul>
        <li>
            jednotlivé části cesty
            <example lang="python">
                >>> p.parts
                ('/', 'tmp', 'pokus', 'tmp.tar.gz')
            </example>
        </li>
        <li>
            informace o kořeni (liší se u Windows a Linuxu)
            <example lang="python">
                >>> p.drive
                ''
                >>> p.anchor
                '/'
                >>> p.root
                '/'
            </example>
        </li>
        <li>
            nadřazené části cesty
            <example lang="python">
                >>> p.parent
                PurePosixPath('/tmp/pokus')

                >>> list(p.parents)
                [PurePosixPath('/tmp/pokus'), PurePosixPath('/tmp'), PurePosixPath('/')]
            </example>
        </li>
        <li>
            informace o jméně souboru
            <example lang="python">
                >>> p.name
                'tmp.tar.gz'
                >>> p.stem
                'tmp.tar'
                >>> p.suffix
                '.gz'
                >>> p.suffixes
                ['.tar', '.gz']
            </example>
        </li>
    </ul>

</slide>
<slide title="Řetězcová reprezentace cesty">

    <example lang="python">
        >>> p = PurePath('/tmp/pokus/tmp.tar.gz')

        >>> p.as_posix()
        '/tmp/pokus/tmp.tar.gz'
        
        >>> p.as_uri()
        'file:///tmp/pokus/tmp.tar.gz'
    </example>

</slide>
<slide title="Relativní cesta">

    <example lang="python">
        >>> p = PurePath('/tmp/pokus/tmp.tar.gz')

        >>> p.relative_to('/tmp')
        PurePosixPath('pokus/tmp.tar.gz')

        >>> p.relative_to( PurePath('/tmp') )
        PurePosixPath('pokus/tmp.tar.gz')
    </example>

</slide>
<slide title="Filtrování cest – „match()“">

    <p>
        Test proti <a href="?slajd=19"><em>glob</em>-vzoru</a>:
    </p>
    <example lang="python">
        >>> p = PurePath('/tmp/pokus/tmp.tar.gz')

        >>> p.match('*.gz')
        True
        >>> p.match('tmp/*.gz')
        False
        >>> p.match('pokus/*.gz')
        True
    </example>

</slide>
<slide title="Test absolutnosti (a rezervace)">

    <p>
        Pokud má cesta <em>root</em> a v případě Windows i <em>drive</em>, je pokládána za absolutní:
    </p>
    <example lang="python">
        >>> PurePath('/tmp/pokus/tmp.tar.gz').is_absolute()
        True

        >>> PurePath('pokus/tmp.tar.gz').is_absolute()
        False
    </example>
    <p>
        PS: Na Windows mohou také existovat tzv. <em>rezervované cesty</em>, operace s nimiž jsou zcela nedefinované a plné vedlejších efektů. O nich se dozvíte pomocí metody <em>is_reserved()</em>.
    </p>

</slide>
<slide title="Přejmenovávání">

    <p>
        Následující operace vyrobí na základě existující cesty cestu s novou příponou či jménem:
    </p>
    <example lang="python">
        >>> p = PurePath('/tmp/pokus/tmp.tar.gz')

        # změna přípony
        >>> p.with_suffix('.bz2')
        PurePosixPath('/tmp/pokus/tmp.tar.bz2')

        # změna jména
        >>> p.with_name('tmp.tgz')
        PurePosixPath('/tmp/pokus/tmp.tgz')
    </example>
    <p>
        PS: Pokud zadaná cesta nemá jméno (tedy atribut <em>name</em> je prázdný řetězec), obdržíte výjimku <em>ValueError</em>.
    </p>

</slide>
<slide title="B) Konkrétní cesty">

    <p>
        Narozdíl od abstraktních mají <strong><em>konkrétní cesty</em></strong> již přístup na souborový systém, tedy operací s nimi již může docházet k jeho změnám.
    </p>
    <note>
        PS: Podobně jako u abstraktních cest je zde základní třídou <em>pathlib.Path</em>, která se podle aktuálního operačního systému realizuje buď jako <em>PosixPath</em> nebo <em>WindowsPath</em>. Narozdíl od abstraktních si ale obě konkrétnější třídy <strong>ne</strong>můžete libovolně zavolat! Je povolena pouze ta, která se týká vašeho systému.
    </note>
    <p>
        PS: Manipulace s reálnými cestami pochopitelně podléhá pravidlům příslušného operačního systému, takže sice kupříkladu můžete při vytváření souborů nastavovat jejich přístupová práva a další vlastnosti, ale pouze v tom rozsahu, jaký máte přidělený pro daný běh pythoního interpretru.
    </p>

</slide>
<slide title="Vytvoření cesty">

    <p>
        Vytvoření konkrétní cesty je úplně stejné jako <a href="?slajd=5">vytvoření abstraktní cesty</a>, jen nejpoužívanějším konstruktorem (a nejpoužívanějším prvkem celého modulu <em>pathlib</em> vůbec) je nyní třída <strong><em>Path</em></strong>:
    </p>
    <example lang="python">
        >>> from pathlib import Path

        >>> p = Path('/tmp/pokus') / 'tmp.txt'
        >>> p
        PosixPath('/tmp/pokus/tmp.txt')
    </example>
    <p>
        Všechny ostatní možné způsoby vytvoření cesty fungují samozřejmě také.
    </p>

</slide>
<slide title="Získání cesty">

    <p>
        K dispozici jsou dvě pomocné funkce, jak dosáhnout na konkrétní cestu:
    </p>
    <ul>
        <li>
            aktuální pracovní adresář
            <example lang="python">
                >>> Path().cwd()
                PosixPath('/tmp')
            </example>
        </li>
        <li>
            uživatelův domovský adresář
            <example lang="python">
                >>> Path().home()
                PosixPath('/home/pirat')
            </example>
        </li>
    </ul>
    <p>
        PS: Nedává asi úplně moc smysl, že tyto metody nejsou statické (zvláště <em>home()</em>, který se prakticky nedá změnit).
    </p>
    <handout>
>>> from pathlib import Path as P
>>> P().cwd()
PosixPath('/home/pirat')
>>> p = P('/tmp')
>>> p
PosixPath('/tmp')
>>> p.cwd()
PosixPath('/home/pirat')
>>> P().cwd()
PosixPath('/home/pirat')
    </handout>

</slide>
<slide title="Normalizace cesty">

    <p>
        Podobně máme několik funkcí k úpravě cesty na normalizovaný tvar:
    </p>
    <ul>
        <li>
            <strong><em>expanduser()</em></strong> – náhrada zkratky domovského adresáře <code>~</code> za celou cestu
            <example lang="python">
                >>> p = Path('~/tmp')
                >>> p
                PosixPath('~/tmp')

                >>> p.expanduser()
                PosixPath('/home/pirat/tmp')
            </example>
        </li>
        <li>
            <strong><em>resolve()</em></strong> – převod cesty na kanonickou (včetně náhrady symlinků a eliminace <code>'..'</code>)
            <example lang="python">
                >>> Path().cwd()
                PosixPath('/tmp')

                >>> p = Path('./pokus/..')
                >>> p
                PosixPath('pokus/..')

                >>> p.resolve()
                PosixPath('/tmp')
            </example>
            <note>
                Od Python'u 3.6 můžete změnou výchozího nastavení parametru <em>strict=False</em> na <code>True</code> zajistit vyhození výjimky <em>FileNotFoundError</em>, pokud cesta neexistuje. Pokus o zkanoničtění cesty vedoucí k nekonečné smyčce vyhodí prozměnu <em>RuntimeError</em>.
            </note>
        </li>
        <li>
            <strong><em>absolute()</em></strong> – převod cesty na absolutní (ovšem s ponecháním <code>'.'</code> a <code>'..'</code>!)
            <example lang="python">
                >>> p = Path('./pokus/..')
                >>> p
                PosixPath('pokus/..')

                >>> p.absolute()
                PosixPath('/tmp/pokus/..')
            </example>
            <note>
                PS: Podle poznámek ve zdrojovém kódu tato funkce ještě není odladěna…
            </note>
        </li>
    </ul>
    <p>
    </p>

</slide>
<slide title="Informace o cestě I">

    <p>
        Existence, vlastníci, časové značky a další vlastnosti:
    </p>
    <example lang="python">
        >>> p = Path('/tmp/pokus/tmp.txt')
        >>> p
        PosixPath('/tmp/pokus/tmp.txt')

        # Existuje vůbec cesta?
        >>> p.exists()
        True

        # vlastník a skupina
        >>> p.owner()
        'pirat'
        >>> p.group()
        'pirat'

        # vlastnosti cesty (pro symlink použijte metodu „lstat()“)
        # PS: výstup úmyslně zalámán a okomentován
        >>> p.stat()
        os.stat_result(
            # práva inode'u
            st_mode=33188,
            # číslo inode'u a zařízení, na němž se nachází
            st_ino=1839445, st_dev=2049,
            # počet odkazů na inode
            st_nlink=1,
            # ID vlastníka a skupiny
            st_uid=1000, st_gid=1000, 
            # (většinou) velikost souboru v bajtech
            st_size=29, 
            # čas přístupu, modifikace a změny metadat (případně vytvoření)
            st_atime=1520868343, st_mtime=1520868300, st_ctime=1520868300
        )

        # Jedná se o stejnou cestu? (lze zadat různě, třebas i řetězcem)
        >>> Path('pokus/tmp.txt')
        PosixPath('pokus/tmp.txt')
        >>> p.samefile( Path('pokus/tmp.txt') )
        True
    </example>

</slide>
<slide title="Modul „stat“ – jak číst výstup metody „Path.stat()“">

    <p>
        Většina údajů vrácených z metody <em>Path.stat()</em> je celkem jasných, byť občas s nějakou poznámkou:
    </p>
    <ul>
        <li>
            <code>st_mode</code> – přístupová práva k příslušnému <em>inode</em>u jako číslo; pokud preferujete spíše řetězec, je na to v modulu <a href="https://docs.python.org/3/library/stat.html" class="external"><code>stat</code></a> šikovná funkce jménem <code>filemode()</code>:
            <example lang="python">
                >>> from stat import filemode
                >>> filemode(33188)
                '-rw-r--r--'
            </example>
        </li>
        <li>
            <code>st_atime</code>, <code>st_mtime</code>, <code>st_ctime</code> – popořadě čas posledního přístupu k souboru (<em><b>a</b>ccess</em>), jeho poslední modifikace (<em><b>m</b>odification</em>) a čas poslední změny metadat či vytvoření souboru (<em><b>c</b>reation</em>; konkértní význam je tedy závislý na platformě) v sekundách od <a href="/materialy/python/modules/times+dates.xml?slajd=2">začátku (unixové) epochy</a>:
            <example lang="python">
                >>> from datetime import datetime
                >>> datetime.fromtimestamp(1520868343)
                datetime.datetime(2018, 3, 12, 16, 25, 43)
            </example>
        </li>
    </ul>
    <p>
        Co si ale vyžaduje podrobnější vysvětlení, jsou samotné vlastnosti souboru skryté jako číslo pod parametrem <strong><em>st_mode</em></strong>:
    </p>
    <example layout="vertical">
        <program src="_files/modetest.py" lang="python"/>
        <out src="_files/modetest.out" lang="text"/>
    </example>
    <note>
        V principu jde tedy o bitovou masku údajů o souboru (aneb každý nastavený bit hlásí nějakou vlastnost), která je – pochopitelně – z části závislá na použitém operačním systému. Více viz dokumentace k modulu <a href="https://docs.python.org/3/library/stat.html" class="external"><code>stat</code></a>.
    </note>

</slide>
<slide title="Informace o cestě II">

    <ul>
        <li>
            Dotazy na adresáře, soubory a symbolické odkazy:
            <example lang="python">
                >>> Path('pokus').is_dir()
                True

                >>> Path('tmp.txt').is_dir()
                False
                >>> Path('tmp.txt').is_file()
                True

                >>> Path('tmp.tar.gz').is_symlink()
                True
            </example>
        </li>
        <li>
            Další typy dotazů – <em>is_block_device()</em>, <em>is_char_device()</em>, <em>is_fifo()</em>, <em>is_socket()</em> .
            <!--example lang="python">
            </example-->
        </li>
    </ul>

</slide>
<slide title="Získání podcest – „iterdir()“">

    <p>
        Získání seznamu podcest aktuální cesty (tedy typicky podadresářů, souborů a symbolických odkazů v daném adresáři) zařizuje generátor <strong><em>iterdir()</em></strong>:
    </p>
    <example lang="python">
        >>> p = Path('pokus')

        >>> p.iterdir()
        &lt;generator object Path.iterdir at 0x7f6ea0eee468>

        # nazvdory tomu, že se výpis tváří jako řetězce..
        >>> for i in p.iterdir():
        ...   print(i)
        ... 
        pokus/tmp.txt
        pokus/tmp.tar.gz

        # ..jde ve skutečnosti o instance příslušné třídy „Path“
        >>> list( p.iterdir() )
        [PosixPath('pokus/tmp.txt'), PosixPath('pokus/tmp.tar.gz')]
    </example>
    <p>
        Tohle se zjevně mimo jiné dobře kombinuje s předchozími <em>is_</em>-metodami:
    </p>
    <example lang="python">
        >>> for i in p.iterdir():
        ...   if i.is_file():
        ...     print(i)
        ... 
        pokus/tmp.txt
        pokus/tmp.tar.gz
    </example>

</slide>
<slide title="Získání podcest – „glob()“ &amp; „rglob()“">

    <p>
        Pokud chceme proiterovat pouze cesty, které vyhovují nějakému <strong>vzoru</strong>, máme k dispozici další metodu – <em>glob()</em>. Ta reaguje na klasické „divoké znaky“ <code>?</code>, <code>*</code> a znakové rozsahy <code>[]</code>:
    </p>
    <example lang="python">
        >>> p = Path('/tmp')

        >>> list(p.glob('pokus/*'))
        [PosixPath('/tmp/pokus/tmp.tar.gz'), PosixPath('/tmp/pokus/tmp.txt'),
         PosixPath('/tmp/pokus/2.nfo'), PosixPath('/tmp/pokus/pokus.txt'),
         PosixPath('/tmp/pokus/1.txt')]

        >>> list(p.glob('pokus/*.txt'))
        [PosixPath('/tmp/pokus/tmp.txt'), PosixPath('/tmp/pokus/pokus.txt'),
         PosixPath('/tmp/pokus/1.txt')]

        >>> list(p.glob('pokus/[0-9].*'))
        [PosixPath('/tmp/pokus/2.nfo'), PosixPath('/tmp/pokus/1.txt')]
    </example>
    <notes>
        <note>
            Chcete-li najít soubor/adresář s <code>?</code> nebo <code>*</code> v názvu, stačí je zavřít do <code>[]</code>.
        </note>
        <note>
            Výstup zalámán pro lepší čitelnost.
        </note>
    </notes>
    <p>
        PS: Speciální dvojznak <code>**</code> označuje <strong>aktuální adresář a rekurzivně i všechny jeho podadresáře</strong>. Varianta <em>rglob()</em> se pak chová jako <em>glob()</em> s automaticky přidaným tímto dvojznakem na začátek vzoru.
    </p>

</slide>
<slide title="Změna vlastností – „chmod()“ / „lchmod()“">

    <p>
       Pomocí funkce <em>chmod()</em> můžete změnit práva a vlastnosti odkazované cesty:
    </p>
    <example lang="python">
        >>> p = Path('/tmp/pokus/pokus.txt')

        >>> p.stat().st_mode
        33279
        >>> oct(33279)
        '0o100777'

        >>> p.chmod(0o644)
        >>> p.stat().st_mode
        33188
        >>> oct(33188)
        '0o100644'
    </example>
    <note>
        Pro udání práv/vlastností můžete kromě čísel také využít příslušné konstanty z modulu <a href="https://docs.python.org/3/library/stat.html" class="external"><em>stat</em></a> (kombinace se zadávají klasicky binárním OŘením).
    </note>
    <p>
        PS: Je-li cesta symbolickým odkazem, změní funkce <em>chmod()</em> vlastnosti odkazované cesty. <strong>Chcete-li změnit vlastnosti symbolického odkazu, použijte funkci <em>lchmod()</em>.</strong>
    </p>

</slide>
<slide title="Tvorba a mazání souborů – „touch()“ &amp; „unlink()“">

    <p class="enumerate">
        Soubor (prázdný) vytvoříme klasicky unixovsky pomocí funkce <strong><em>touch(mode=0o666, exist_ok=True)</em></strong>:
    </p>
    <example lang="python">
        # Zatím je adresář prázdný..
        >>> p = Path('/tmp/pokus')
        >>> list( p.iterdir() )
        []

        # ..ale teď už nebude:
        >>> Path('pokus.txt').touch()
        >>> list( p.iterdir() )
        [PosixPath('/tmp/pokus/pokus.txt')]
    </example>
    <note>
        Parametr <em>exist_ok</em> v základním nastavení na <code>True</code> u existujícího souboru upraví jeho čas modifikace na aktuální, při nastavení na <code>False</code> vyhodí výjimku <em>FileExistsError</em>.
    </note>

    <p class="enumerate">
        Už asi nepřekvapivě se pak soubory mažou pomocí funkce <strong><em>unlink()</em></strong>:
    </p>
    <example lang="python">
        >>> Path('pokus.txt').unlink()
        >>> list( p.iterdir() )
        []
    </example>

</slide>
<slide title="Tvorba a mazání adresářů – „mkdir()“ &amp; „rmdir()“">

    <p class="enumerate">
        Zdánlivě jasná funkce pro tvorbu adresářů <em>mkdir(mode=0o777, parents=False, exist_ok=False)</em> se snaží chovat co nejvíce jako unixový příkaz <code>mkdir -p</code>, takže ve výsledku je její funkcionalita sice velmi užitečná, nicméně už <a href="https://docs.python.org/3/library/pathlib.html#pathlib.Path.mkdir" class="external">zdaleka ne tak jasná</a>. Nicméně základní použití je jednoduché:
    </p>
    <example lang="python">
        # zadání cesty řetězcem
        >>> Path('test1').mkdir()
        
        # zadání cesty objektem Path()
        >>> Path( Path('test2') ).mkdir()
    </example>
    <p>
        Stačí si tedy jen dobře uvědomit, že <em>mkdir()</em> je vlastnost cesty – volá se tudíž na objektu <em>Path</em> a případné parametry této funkce pouze řídí její chování.
    </p>

    <p class="enumerate">
        S funkcí opačnou, totiž mazání adresářů pomocí <em>rmdir()</em>, je to naštěstí jednodušší – představuje-li zadaná cesta prázdný adresář, bude tento smazán, jinak dojde k výjimce:
    </p>
    <example lang="python">
        # prázdný adresář
        >>> Path('test2').rmdir()

        # neprázdný adresář
        >>> Path('test1').rmdir()
        Traceback (most recent call last):
          …
        OSError: [Errno 39] Directory not empty: 'test1'
    </example>
    <note>
        Cestu lze opět pochopitelně zadat jak řetězcem, tak objektem typu <em>Path</em>.
    </note>

</slide>
<slide title="Přejmenovávání (přesouvání) cest – „replace()“">

    <p>
        Jelikož většina funkcí tohoto modulu jsou vlastně jenom <em>wrapper</em>y nad odpovídajícími funkcemi modulu <em>os</em>, nepřekvapí tudíž, že doporučovaným nezávisle na platformě operujícím příkazem pro přejmenování cesty jest funkce <strong><em>replace()</em></strong>:
    </p>
    <example lang="python">
        # Původní cesta..
        >>> p = Path('/tmp/pokus/test1')

        # ..a její přejmenování:
        >>> p.replace('test')

        # Objekt původní cesty už neukazuje na existující objekt..
        >>> p
        PosixPath('/tmp/pokus/test1')
        >>> p.exists()
        False

        # ..protože ten už má své nové jméno:
        >>> Path('/tmp/pokus/test').exists()
        True
    </example>
    <note>
        K určení cesty se opět pochopitelně dá použít místo řetězce i přímo objekt typu <em>Path</em>.
    </note>
    <p>
        PS: Tím druhým příkazem, více unixově zaměřeným, jest <em>rename()</em>.
    </p>

</slide>
<slide title="Kopírování souborů – „shutil.copy2()“">

    <p>
        Ačkoli se pomocí modulu <em>pathlib</em> dají <a href="?slajd=24">soubory přesouvat</a> (prostě změníte cestu daného souboru, čímž změníte jeho umístění), nedají se kupodivu kopírovat. K tomu slouží podivně pojmenovaná funkce z modulu <em>shutil</em>:
    </p>
    <blockquote>
        shutil.copy2(ZDROJ, CÍL)
    </blockquote>
    <note>
        Oba parametry cesty mohou nepřekvapivě být buď <em>Path</em>-objekty nebo řetězce.
    </note>
    <p>
        Chcete-li zachovat jméno souboru, stačí jako <code>CÍL</code> použít pouze cílový adresář. Chcete-li jméno souboru změnit (nebo tak prostě potřebujete mít napsaný kód), jako <code>CÍL</code> použijte celou cestu včetně (nového) jména souboru.
    </p>
    <p>
        PS: Existuje i metoda <em>shutil.copy()</em> (a kromě ní ještě dvě další podobné), ale ta nekopíruje většinu metadat souboru (vlastně zkopíruje pouze jeho jméno, obsah a přístupová práva). Většinou nechcete přijít třebas o čas modifikace souboru, takže použijete spíš právě <em>copy2()</em>, i když to vypadá divně.
    </p>

</slide>
<slide title="Symbolický odkaz – „symlink_to()“">

    <p>
        Symbolický odkaz na existující cestu vyrobíme pomocí <strong><em>Path(SYMLINK).symlink_to(CESTA)</em></strong>:
    </p>
    <example lang="python">
        # Existující cesta..
        >>> list( p.iterdir() )
        [PosixPath('/tmp/pokus/pokus.txt')]

        # ..a symlink na ni:
        >>> Path('pokus_sln.txt').symlink_to('pokus.txt')
        >>> list( p.iterdir() )
        [PosixPath('/tmp/pokus/pokus_sln.txt'), PosixPath('/tmp/pokus/pokus.txt')]
    </example>
    <p>
        PS: Plný konstruktor jest <code>symlink_to(target, target_is_directory=False)</code>, kde parametr <em>target_is_directory</em> má význam pouze pod Windows (musí být nastaven na <code>True</code>, je-li cílem symlinku adresář).
    </p>
    <note>
        Aneb další nekonzistentní místo komplikující psaní přenositelného kódu… Pravda, Linux tento atribut ignoruje, takže ho tam můžeme uvést vždycky, ale stejně.
    </note>

</slide>
<slide title="Práce se soubory – „open()“">

    <p>
        Když už máme cestu připravenou jako objekt typu <em>Path</em>, můžeme ji téměř klasicky otevřít k práci s jejím obsahem pomocí funkce <strong><em>open()</em></strong>:
    </p>
    <example lang="python">
        >>> f = Path('pokus.txt')
        >>> with f.open(encoding='utf-8') as soubor:
        ...   print( soubor.read() )
        ... 
        příliš žluťoučký kůň
    </example>
    <p>
        Její parametry jsou od druhého dále nepřekvapivě úplně stejné jako u standardní funkce <a href="basics.xml?slajd=4"><em>open()</em></a>:
    </p>
    <blockquote>
        open(mode='r', buffering=-1, encoding=None, errors=None, newline=None)
    </blockquote>
    <p>
        PS: Při neudání kódování si bohužel Python klasicky doplní výchozí z aktuálního systému :-(
    </p>

</slide>
<slide title="Práce se soubory – vlastní metody">

    <p>
        Kromě toho ale existují i dedikované metody pro práci s obsahem cest, a to jak v textové, tak binární variantě:
    </p>

    <p class="enumerate">
        Při práci s textem máme při zápise i při čtení k dispozici možnost nastavit jak kódování (parametr <em>encoding</em>), tak <a href="texts.xml?slajd=11">obsluhu chyb</a> (parametr <em>errors</em>). Neexistující soubor bude při zápise založen:
    </p>
    <example lang="python">
        >>> f = Path('pokus.txt')
        >>> f.write_text('příliš žluťoučký kůň', encoding='utf-8')
        20
        >>> f.read_text()
        'příliš žluťoučký kůň'
    </example>
    <note>
        Plné konstruktory tedy jsou: <code>Path.write_text(data, encoding=None, errors=None)</code> a <code>Path.read_text(encoding=None, errors=None)</code> .
    </note>
    <p>
        A opět – při neudání kódování si bohužel Python klasicky doplní výchozí z aktuálního systému. Jinak funkce <em>write_text()</em> při zápise vrací <strong>počet zapsaných znaků</strong>.
    </p>

    <p class="enumerate">
        Při práci s bajty se žádné kódování neřeší, takže prostě jen zapisujeme a čteme <a href="/materialy/python/types/bytes.xml">bajtové řetězce</a>, jinak je vše stejné:
    </p>
    <example lang="python">
        >>> f = Path('pokus.nfo')
        >>> f.write_bytes(b'ahoj')
        4
        >>> f.read_bytes()
        b'ahoj'
    </example>
    <p>
        A stejně tak funkce <em>write_bytes()</em> při zápise vrací <strong>počet zapsaných bajtů</strong>.
    </p>

</slide>


</lecture>
