Мега-учебник flask, часть 4: база данных (издание 2018)

Обработка ошибок Blueprint

Первая созданная мной схема элементов была инкапсулирована в поддержку обработчиков ошибок. Структура этой концепции такова:


В сущности, я переместил модуль app/errors.py в app/errors/handlers.py и два шаблона ошибок в app/templates/errors, чтобы они были отделены от других шаблонов. Мне также пришлось изменить вызовы в обоих обработчиках ошибок, чтобы использовать подкаталог нового шаблона ошибок. После этого я добавил создание blueprint в модуль и регистрацию проекта в после создания экземпляра приложения.

Я должен отметить, что схемы элементов Flask могут быть настроены на отдельный каталог для шаблонов или статических файлов. Я решил переместить шаблоны в подкаталог каталога шаблонов приложения, чтобы все шаблоны находились в одной иерархии, но если вы предпочитаете иметь шаблоны, принадлежащие схеме элементов внутри пакета схемы элементов, это поддерживается. Например, если добавить аргумент в конструктор , можно сохранить шаблоны схемы элементов в app/errors/templates.

Создание схемы элементов сильно похоже на создание приложения. Это делается в модуле пакета blueprint:

Класс принимает имя схемы элементов, имя базового модуля (обычно устанавливается в , как в экземпляре приложения Flask), и несколько необязательных аргументов, которые в этом случае мне не нужны. После создания объекта схемы элементов я импортирую handlers.py модуль, чтобы обработчики ошибок в нем были зарегистрированы в схеме элементов. Этот импорт находится внизу, чтобы избежать циклических зависимостей.

В модуле handlers.py вместо прикрепления обработчиков ошибок к приложению с помощью декоратора я использую blueprint-декоратор . Хотя оба декоратора достигают одного и того же конечного результата, идея состоит в том, чтобы попытаться сделать схему элементов независимой от приложения, чтобы она была более портативной. Мне также нужно изменить путь к двум шаблонам ошибок, чтобы учесть новый подкаталог ошибок, в который они были перемещены.

Последним шагом для завершения рефакторинга обработчиков ошибок является регистрация схемы элементов в приложении:

Для регистрации схемы элементов используется метод экземпляра приложения Flask. При регистрации схемы элементов все функции представления, шаблоны, статические файлы, обработчики ошибок и т. д. подключаются к приложению. Я помещаю импорт схемы элементов прямо над , чтобы избежать циклических зависимостей.

11.1.1. Basic use¶

Importing the main class:

>>> from pathlib import Path

Listing subdirectories:

>>> p = Path('.')
>>> x for x in p.iterdir() if x.is_dir()]

Listing Python source files in this directory tree:

>>> list(p.glob('**/*.py'))
[PosixPath('test_pathlib.py'), PosixPath('setup.py'),
 PosixPath('pathlib.py'), PosixPath('docs/conf.py'),
 PosixPath('build/lib/pathlib.py')]

Navigating inside a directory tree:

>>> p = Path('/etc')
>>> q = p  'init.d'  'reboot'
>>> q
PosixPath('/etc/init.d/reboot')
>>> q.resolve()
PosixPath('/etc/rc.d/init.d/halt')

Querying path properties:

>>> q.exists()
True
>>> q.is_dir()
False

Opening a file:

File Times¶

Besides working with paths, includes functions for retrieving file properties, similar to the ones returned by :

ospath_properties.py

import os.path
import time

print('File         :', __file__)
print('Access time  :', time.ctime(os.path.getatime(__file__)))
print('Modified time:', time.ctime(os.path.getmtime(__file__)))
print('Change time  :', time.ctime(os.path.getctime(__file__)))
print('Size         :', os.path.getsize(__file__))

returns the access time, returns the modification time, and returns the creation time. returns the amount of data in the file, represented in bytes.

Базовые

Управляет авто-обновлением ядра:

  • true — включить все обновления — версии разработчиков, незначительные и значительные релизы.
  • false — отключить все обновления ядра — версии разработчиков, незначительные и значительные релизы.
  • ‘minor’ — включить обновления только для незначительных релизов (внутри ветки).

Может быть:

Авто-обновление движка, введенное в версии 3.7. По умолчанию константа не определена (false — авто-обновление включено). Чтобы отключить авто-обновление установите эту константу, укажите ей значение true:

define( 'AUTOMATIC_UPDATER_DISABLED', true );

Может быть:

Интервал автосохранения постов при редактировании.Может быть: По умолчанию:
true — затрагивать новые группы файлов (плагины или темы) при обновлении.Может быть:
Деактивирует работу cron (планировщик заданий) в WordPress.Может быть:
Количество дней до удаления записи (страница, вложение, пост, комментарий и т.д.) из корзины. Ставим false в wp-config.php, если нужно отключить корзину, записи будут удаляться при удалении безвозвратно.Может быть: По умолчанию:
false — будут создаваться новые изображения при редактировании — копии оригинала. true — старое изображение будет перезаписано.Может быть:
Функция «корзины» для медиафайлов. true — корзина работает. false — не работает.Может быть: По умолчанию:
Определяет файл локализации WordPress. Нужен для перевода сайта на нужный язык.Может быть:
Устанавливает тему для новых сайтов по умолчанию. И используется как запасной вариант для сломанных тем WordPress.Может быть: По умолчанию:
Устанавливает минимальный интервал времени, между выполнениями cron запросов. С версии 3.3.Может быть: По умолчанию:
Определяет интервал времени, в течении которого только один mail-запрос может быть выполнен.Может быть: По умолчанию:

Ревизии записей:

  • true — включены
  • false — отключены
  • число — число ревизий записи. 5 — запись будет содержать максимально 5 ревизий.

Может быть: По умолчанию:

Максимальное ограничение памяти для некоторых функций WordPress.По умолчанию:
Ограничение памяти на выполнение скриптов WordPress.По умолчанию:

Deriving new paths

A path can be joined with another using the operator:

>>> p = PurePosixPath('foo')
>>> p / 'bar'
PurePosixPath('foo/bar')
>>> p / PurePosixPath('bar')
PurePosixPath('foo/bar')
>>> 'bar' / p
PurePosixPath('bar/foo')

As with the constructor, multiple path components can be specified, either collapsed or separately:

>>> p / 'bar/xyzzy'
PurePosixPath('foo/bar/xyzzy')
>>> p / 'bar' / 'xyzzy'
PurePosixPath('foo/bar/xyzzy')

A joinpath() method is also provided, with the same behaviour:

>>> p.joinpath('Python')
PurePosixPath('foo/Python')

The with_name() method returns a new path, with the name changed:

>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
>>> p.with_name('setup.py')
PureWindowsPath('c:/Downloads/setup.py')

It fails with a ValueError if the path doesn’t have an actual name:

>>> p = PureWindowsPath('c:/')
>>> p.with_name('setup.py')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pathlib.py", line 875, in with_name
    raise ValueError("%r has an empty name" % (self,))
ValueError: PureWindowsPath('c:/') has an empty name
>>> p.name
''

The with_suffix() method returns a new path with the suffix changed. However, if the path has no suffix, the new suffix is added:

>>> p = PureWindowsPath('c:/Downloads/pathlib.tar.gz')
>>> p.with_suffix('.bz2')
PureWindowsPath('c:/Downloads/pathlib.tar.bz2')
>>> p = PureWindowsPath('README')
>>> p.with_suffix('.bz2')
PureWindowsPath('README.bz2')

os.walk()

Метод os.walk() дает нам возможность для итерации на корневом уровне пути. Это значит, что мы можем назначить путь к этой функции и получить доступ ко всем её подкаталогам и файлам. Используем одну из папок Пайтон, при помощи которой мы можем проверить данную функцию. Мы используем C:\Python27\Tools

Python

import os

path = r’C:\Python27\Tools’

for root, dirs, files in os.walk(path): print(root)

1 2 3 4 5 6

importos

path=r’C:\Python27\Tools’

forroot,dirs,files inos.walk(path)

print(root)

Результат работы:

Python

C:\Python27\Tools C:\Python27\Tools\i18n C:\Python27\Tools\pynche C:\Python27\Tools\pynche\X C:\Python27\Tools\Scripts C:\Python27\Tools\versioncheck C:\Python27\Tools\webchecker

1 2 3 4 5 6 7

C:\Python27\Tools C:\Python27\Tools\i18n C:\Python27\Tools\pynche C:\Python27\Tools\pynche\X C:\Python27\Tools\Scripts C:\Python27\Tools\versioncheck C:\Python27\Tools\webchecker

Мы можем отобразить и весь список каталогов и файлов.

Python

import os

for root, dirs, files in os.walk(path): print(root) for _dir in dirs: print(_dir)

for _file in files: print(_file)

1 2 3 4 5 6 7 8 9

importos

forroot,dirs,files inos.walk(path)

print(root)

for_dir indirs

print(_dir)

for_file infiles

print(_file)

Эта часть кода покажет нам весьма большой объем, так что не будем демонстрировать всю выдачу, вы можете ознакомиться с содержимым сами. Поздравляем, теперь мы готовы к тому, чтобы научиться работать с путями!

Pure paths¶

Pure path objects provide path-handling operations which don’t actually access a filesystem. There are three ways to access these classes, which we also call flavours:

class (*pathsegments)

A generic class that represents the system’s path flavour (instantiating it creates either a or a ):

>>> PurePath('setup.py')      # Running on a Unix machine
PurePosixPath('setup.py')

Each element of pathsegments can be either a string representing a path segment, an object implementing the interface which returns a string, or another path object:

>>> PurePath('foo', 'some/path', 'bar')
PurePosixPath('foo/some/path/bar')
>>> PurePath(Path('foo'), Path('bar'))
PurePosixPath('foo/bar')

When pathsegments is empty, the current directory is assumed:

>>> PurePath()
PurePosixPath('.')

When several absolute paths are given, the last is taken as an anchor (mimicking ’s behaviour):

>>> PurePath('/etc', '/usr', 'lib64')
PurePosixPath('/usr/lib64')
>>> PureWindowsPath('c:/Windows', 'd:bar')
PureWindowsPath('d:bar')

However, in a Windows path, changing the local root doesn’t discard the previous drive setting:

>>> PureWindowsPath('c:/Windows', '/Program Files')
PureWindowsPath('c:/Program Files')

Spurious slashes and single dots are collapsed, but double dots () are not, since this would change the meaning of a path in the face of symbolic links:

>>> PurePath('foo//bar')
PurePosixPath('foo/bar')
>>> PurePath('foo/./bar')
PurePosixPath('foo/bar')
>>> PurePath('foo/../bar')
PurePosixPath('foo/../bar')

(a naïve approach would make equivalent to , which is wrong if is a symbolic link to another directory)

Pure path objects implement the interface, allowing them to be used anywhere the interface is accepted.

Changed in version 3.6: Added support for the interface.

class (*pathsegments)

A subclass of , this path flavour represents non-Windows filesystem paths:

>>> PurePosixPath('/etc')
PurePosixPath('/etc')

pathsegments is specified similarly to .

class (*pathsegments)

A subclass of , this path flavour represents Windows filesystem paths:

>>> PureWindowsPath('c:/Program Files/')
PureWindowsPath('c:/Program Files')

pathsegments is specified similarly to .

Regardless of the system you’re running on, you can instantiate all of these classes, since they don’t provide any operation that does system calls.

General properties

Paths are immutable and hashable. Paths of a same flavour are comparable and orderable. These properties respect the flavour’s case-folding semantics:

>>> PurePosixPath('foo') == PurePosixPath('FOO')
False
>>> PureWindowsPath('foo') == PureWindowsPath('FOO')
True
>>> PureWindowsPath('FOO') in { PureWindowsPath('foo') }
True
>>> PureWindowsPath('C:') < PureWindowsPath('d:')
True

Paths of a different flavour compare unequal and cannot be ordered:

>>> PureWindowsPath('foo') == PurePosixPath('foo')
False
>>> PureWindowsPath('foo') < PurePosixPath('foo')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: '<' not supported between instances of 'PureWindowsPath' and 'PurePosixPath'

Operators

The slash operator helps create child paths, similarly to :

>>> p = PurePath('/etc')
>>> p
PurePosixPath('/etc')
>>> p  'init.d'  'apache2'
PurePosixPath('/etc/init.d/apache2')
>>> q = PurePath('bin')
>>> '/usr'  q
PurePosixPath('/usr/bin')

A path object can be used anywhere an object implementing is accepted:

>>> import os
>>> p = PurePath('/etc')
>>> os.fspath(p)
'/etc'

The string representation of a path is the raw filesystem path itself (in native form, e.g. with backslashes under Windows), which you can pass to any function taking a file path as a string:

>>> p = PurePath('/etc')
>>> str(p)
'/etc'
>>> p = PureWindowsPath('c:/Program Files')
>>> str(p)
'c:\\Program Files'

Similarly, calling on a path gives the raw filesystem path as a bytes object, as encoded by :

>>> bytes(p)
b'/etc'

Note

Calling is only recommended under Unix. Under Windows, the unicode form is the canonical representation of filesystem paths.

Accessing individual parts

To access the individual “parts” (components) of a path, use the following property:

A tuple giving access to the path’s various components:

>>> p = PurePath('/usr/bin/python3')
>>> p.parts
('/', 'usr', 'bin', 'python3')

>>> p = PureWindowsPath('c:/Program Files/PSF')
>>> p.parts
('c:\\', 'Program Files', 'PSF')

(note how the drive and local root are regrouped in a single part)

Wildcard support

FindFirstFile/FindNextFile on Windows support passing a «wildcard» like *.jpg, so at first folks (this PEP’s author included) felt it would be a good idea to include a windows_wildcard keyword argument to the scandir function so users could pass this in.

However, on further thought and discussion it was decided that this would be bad idea, unless it could be made cross-platform (a pattern keyword argument or similar). This seems easy enough at first — just use the OS wildcard support on Windows, and something like fnmatch or re afterwards on POSIX-based systems.

Unfortunately the exact Windows wildcard matching rules aren’t really documented anywhere by Microsoft, and they’re quite quirky (see this blog post), meaning it’s very problematic to emulate using fnmatch or regexes.

So the consensus was that Windows wildcard support was a bad idea. It would be possible to add at a later date if there’s a cross-platform way to achieve it, but not for the initial version.

Что такое Операционная Система?

Операционная система (ОС) – самое важное программное обеспечение, которое работает на компьютере. Она управляет компьютерной памятью, процессами и всем прикладным программным обеспечением (программами) и оборудованием

Она также позволяет вам общаться с компьютером, не зная, «компьютерного языка». Без операционной системы вы не сможете использовать компьютер по назначению.

Загрузка операционной системы.

Вы, наверное, слышали фразу загрузить компьютер, но знаете ли вы, что это значит? Загрузка — это процесс, который происходит, когда вы нажимаете кнопку питания, чтобы включить компьютер. Во время этого процесса (который может занять некоторое время), компьютер делает несколько вещей:

  • Выполняет тесты, чтобы убедиться, что все работает правильно.
  • Проверяет наличие нового оборудования.
  • Запускает операционную систему.

Когда операционная система запущена, она управляет всем программным и аппаратным обеспечением на компьютере. В процессе работы компьютера, множество различных программ, работают одновременно, и все они должны иметь доступ к ресурсам компьютера (процессору, оперативной памяти, жёсткому диску). ОС координат всё это, чтобы убедиться, что каждая программа получает, что ей нужно. Без ОС, программное обеспечение не может общаться с оборудованием, а компьютер в таком случае бесполезен.

Текущие Ограничения

  • Подсистема аутентификации пользователя, которая включает некоторые функции просмотра в app/routes.py, некоторые формы в app/forms.py, некоторые шаблоны в app/templates и поддержку электронной почты в app/email.py.
  • Подсистема ошибок, которая определяет обработчики ошибок в app/errors.py и шаблоны в app/templates.
  • Основные функциональные возможности приложения, которые включают в себя отображение и запись сообщений в блогах, профилей пользователей и последующих событий, а также «живые» переводы сообщений в блогах, которые распределены в большом числе модулей приложений и шаблонов.

Думая об этих трех подсистемах, которые я определил, и о том, как они структурированы, вы, вероятно, можете заметить закономерность. До сих пор логика организации, которой я следовал, основана на наличии модулей, посвященных различным функциям приложения. Есть модуль для функций просмотра, еще один для веб-форм, один для ошибок, один для писем, каталог для HTML шаблонов и так далее. Хотя это структура, которая имеет смысл для небольших проектов, как только проект начинает расти, он имеет тенденцию делать некоторые из этих модулей действительно большими и грязными.

Один из способов четко увидеть проблему — рассмотреть, как вы начнете второй проект, повторно используя столько, сколько сможете из первого. Например, часть отвечающая за проверку подлинности пользователя должна хорошо работать и в других приложениях. Но если вы хотите использовать этот код как есть, вам придется перейти в несколько модулей и скопировать/вставить соответствующие разделы в новые файлы в новом проекте. Видишь, как это неудобно? Не было бы лучше, если бы этот проект имел все файлы, связанные с аутентификацией, отделенные от остальной части приложения? blueprints особенность Flask помогает достичь более практичной организации, которая упрощает повторное использование кода.

Вторая проблема не столь очевидна. Экземпляр приложения Flask создается как глобальная переменная в , а затем импортируется множеством модулей приложений. Хотя это само по себе не является проблемой, использование приложения в качестве глобальной переменной может усложнить некоторые сценарии, в частности связанные с тестированием. Представьте, что вы хотите протестировать это приложение в разных конфигурациях. Поскольку приложение определено как глобальная переменная, на самом деле невозможно создать экземпляр двух приложений, использующих разные переменные конфигурации. Другая ситуация, которая не является идеальной, заключается в том, что все тесты используют одно и то же приложение, поэтому тест может вносить изменения в приложение, которые влияют на другой тест, который выполняется позже. В идеале вы хотите, чтобы все тесты выполнялись на первозданном экземпляре приложения.

Фактически вы можете увидеть все это в модуле tests.py. Я прибегаю к трюку изменения конфигурации после того, как она была установлена ​​в приложении, чтобы направить тесты на использование базы данных в памяти вместо базы данных SQLite (по умолчанию) на основе диска. У меня нет другого способа изменить настроенную базу данных, потому что к моменту запуска тестов приложение было уже создано и настроено. Для этой конкретной ситуации изменение конфигурации после того, как она была применена к приложению, кажется, работает нормально, но в других случаях это не поможет, и в любом случае это плохая практика, которая может привести к неясным и трудным для поиска ошибкам.

Лучшим решением было бы не использовать глобальную переменную для приложения, а вместо этого использовать функцию application factory для создания функции во время выполнения. Это будет функция, которая принимает объект конфигурации в качестве аргумента и возвращает экземпляр приложения Flask, сконфигурированный этими настройками. Если бы я мог модифицировать приложение для работы с функцией фабрики приложений, то писать тесты, требующие специальной настройки, стало бы проще, потому что каждый тест мог создать свое собственное приложение.


В этой главе я собираюсь реорганизовать приложение введением схемы элементов для трех подсистем, указанных выше, и функции фабрики приложений. Показать вам подробный список изменений будет нецелесообразно, потому что есть небольшие изменения в почти каждом файле, который является частью приложения, поэтому я собираюсь обсудить шаги, которые я предпринял, чтобы сделать рефакторинг, и вы можете загрузить приложение с этими изменениями.

/languages — переводы

Каталог wp-content/languages присутствует только в том случае, если вы устанавливаете не английскую версию WordPress. В нем содержаться все файлы локализации (перевода) WordPress. Такие файлы имеют расширения:

  • .mo — сжатая версия аналогичного .po файла, которая используется при переводе;
  • .po — исходный файл перевода. Этот файл можно использовать для редактирования перевода. После редактирования его нужно скомпилировать в сжатую версию с расширением .mo.

Также в languages могут находиться специальные поддиректории:

  • /pliugns — содержит переводы плагинов. Файл перевода должен иметь формат: название плагина-локаль.mo, например: akismet-ru_RU.mo. Перед загрузкой своего файла перевода, плагин проверяет наличие файла перевода в этой папке и если он там есть, то используется этот файл перевода, а не родной перевод плагина.

  • /themes — содержит переводы тем. Файл перевода должен иметь формат: название темы-локаль.mo, например: twentyfifteen-ru_RU.mo. Также как и с плагинами — эти файлы имеют больший приоритет перед родными файлами перевода темы.

Unit Testing Improvements

Как я намекнул в начале этой главы, большая часть работы, которую я проделал до сих пор, была направлена на улучшение рабочего процесса модульного тестирования. При выполнении модульных тестов необходимо убедиться, что приложение настроено таким образом, чтобы оно не мешало ресурсам разработки, таким как база данных.

Текущая версия tests.py прибегает к хитрости изменения конфигурации после того, как она была применена к экземпляру приложения, что является опасной практикой, поскольку не все типы изменений будут работать, если это сделано в последний момент. Я хочу, чтобы у меня была возможность указать мою конфигурацию тестирования, прежде чем она будет добавлена в приложение.

Функция теперь принимает класс конфигурации в качестве аргумента. По умолчанию используется класс определенный в config.py, но теперь я могу создать экземпляр приложения, который использует другую конфигурацию, просто передав новый класс в функцию фабрики. Вот пример класса конфигурации, который можно использовать для модульных тестов:

Здесь я создаю подкласс класса приложения и переопределяю конфигурацию SQLAlchemy для использования базы данных SQLite в памяти. Я также добавил атрибут со значением True, который в настоящее время мне не нужен, но может быть полезен, если приложение должно определить, выполняется ли оно в модульных тестах или нет.

Если вы помните, мои модульные тесты основывались на методах и , которые автоматически вызываются платформой модульного тестирования для создания и уничтожения среды, подходящей для выполнения каждого теста. Теперь я могу использовать эти два метода для создания и уничтожения совершенно нового приложения для каждого теста:

Новое приложение будет храниться в , но создания приложения недостаточно, чтобы все работало. Рассмотрим инструкцию , которая создает таблицы базы данных. Экземпляр должен знать, что такое экземпляр приложения, потому что ему нужно получить URI базы данных из , но когда вы работаете с фабрикой приложений, вы на самом деле не ограничены одним приложением, может быть создано гораздо больше одного. Итак, как же узнает, что должен использовать экземпляр , который я только что создал?

Ответ находится в контексте приложения. Помните переменную , которая каким-то образом выступает в качестве прокси для приложения, когда нет глобального приложения для импорта? Эта переменная ищет активный контекст приложения в текущем потоке, и если она находит его, то получает приложение из него. Если контекст отсутствует, значит невозможно узнать, какое приложение активно, поэтому вызывает исключение. Ниже Вы можете увидеть, как это работает в консоли Python. Это должна быть именно консоль, запущенная с помощью , потому что команда , для удобства, автоматически активирует контекст приложения.

Вот в чем секрет! Перед вызовом функций просмотра Flask вызывает контекст приложения, который возвращает и в жизнь. Когда запрос завершен, контекст удаляется вместе с этими переменными. Для вызова для работы с модулем тестирования метода я выдвинул контекст приложения для только что созданного экземпляра приложения, и таким образом может использовать , чтобы узнать, где это база данных. Затем в методе я вывожу контекст, чтобы сбросить все в чистое состояние.

Вы также должны знать, что контекст приложения является одним из двух контекстов, которые использует Flask. Существует также request context, что более конкретно, поскольку это относится к запросу. Когда контекст запроса активируется непосредственно перед обработкой запроса, становятся доступными переменные и Flask, а также Flask-Login.

Building Paths¶

Besides taking existing paths apart, it is frequently necessary to build paths from other strings. To combine several path components into a single value, use :

ospath_join.py

import os.path

PATHS = 
    ('one', 'two', 'three'),
    ('/', 'one', 'two', 'three'),
    ('/one', '/two', '/three'),


for parts in PATHS
    print('{}{!r}'.format(parts, os.path.join(*parts)))

If any argument to join begins with , all of the previous arguments are discarded and the new one becomes the beginning of the return value.

$ python3 ospath_join.py

('one', 'two', 'three') : 'one/two/three'
('/', 'one', 'two', 'three') : '/one/two/three'
('/one', '/two', '/three') : '/three'

It is also possible to work with paths that include “variable” components that can be expanded automatically. For example, converts the tilde () character to the name of a user’s home directory.

ospath_expanduser.py

import os.path

for user in '', 'dhellmann', 'nosuchuser']:
    lookup = '~' + user
    print('{!r:>15}{!r}'.format(
        lookup, os.path.expanduser(lookup)))

If the user’s home directory cannot be found, the string is returned unchanged, as with in this example.

$ python3 ospath_expanduser.py

            '~' : '/Users/dhellmann'
   '~dhellmann' : '/Users/dhellmann'
  '~nosuchuser' : '~nosuchuser'

is more general, and expands any shell environment variables present in the path.

ospath_expandvars.py

import os.path
import os

os.environ'MYVAR' = 'VALUE'

print(os.path.expandvars('/path/to/$MYVAR'))

No validation is performed to ensure that the variable value results in the name of a file that already exists.

DirEntry fields being static with an ensure_lstat option

Another seemingly simpler and attractive option was suggested by Nick Coghlan in this June 2014 python-dev message: make DirEntry.is_X and DirEntry.lstat_result properties, and populate DirEntry.lstat_result at iteration time, but only if the new argument ensure_lstat=True was specified on the scandir() call.

This does have the advantage over the above in that you can easily get the stat result from scandir() if you need it. However, it has the serious disadvantage that fine-grained error handling is messy, because stat() will be called (and hence potentially raise OSError) during iteration, leading to a rather ugly, hand-made iteration loop:

it = os.scandir(path)
while True:
    try:
        entry = next(it)
    except OSError as error:
        handle_error(path, error)
    except StopIteration:
        break

Or it means that scandir() would have to accept an onerror argument — a function to call when stat() errors occur during iteration. This seems to this PEP’s author neither as direct nor as Pythonic as try/except around a DirEntry.stat() call.

Another drawback is that os.scandir() is written to make code faster. Always calling os.lstat() on POSIX would not bring any speedup. In most cases, you don’t need the full stat_result object — the is_X() methods are enough and this information is already known.

Concrete paths¶

Concrete paths are subclasses of the pure path classes. In addition to operations provided by the latter, they also provide methods to do system calls on path objects. There are three ways to instantiate concrete paths:

class (*pathsegments)

A subclass of , this class represents concrete paths of the system’s path flavour (instantiating it creates either a or a ):

>>> Path('setup.py')
PosixPath('setup.py')

pathsegments is specified similarly to .

class (*pathsegments)

A subclass of and , this class represents concrete non-Windows filesystem paths:

>>> PosixPath('/etc')
PosixPath('/etc')

pathsegments is specified similarly to .

class (*pathsegments)

A subclass of and , this class represents concrete Windows filesystem paths:

>>> WindowsPath('c:/Program Files/')
WindowsPath('c:/Program Files')

pathsegments is specified similarly to .

You can only instantiate the class flavour that corresponds to your system (allowing system calls on non-compatible path flavours could lead to bugs or failures in your application):

>>> import os
>>> os.name
'posix'
>>> Path('setup.py')
PosixPath('setup.py')
>>> PosixPath('setup.py')
PosixPath('setup.py')
>>> WindowsPath('setup.py')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "pathlib.py", line 798, in __new__
    % (cls.__name__,))
NotImplementedError: cannot instantiate 'WindowsPath' on your system

С этим читают