Pull to refresh
39
0.1
Максим @danilovmy

Программист разработчик

Send message

Все бы хорошо, и даже возьму на заметку...но.

Покрытие обычно относится к тестированию кода проекта. Инструменты покрытия кода, в данном случае coverage, помогают определить, насколько хорошо код тестирован путем анализа, какие части кода проекта выполняются во время Тестирования.

И если говорить о подсчете coverage в runtime получается, что проект это и есть "тест" для подсчета покрытия этим тестом проекта.

Я знаю, многие тестируют в runtime на клиентах, но, все же, best practices - это создать тест, который симулирует поведение клиента.

Вложенные библиотеки обычно не тестируют, отдавая тестирование на откуп создателям библиотек. А coverage считается относительно объема кода проекта.

Но если очень хочется посчитать покрытие импортируемых библиотек при запуске через командную строку используется ключ --source и после, например, имя импортируемого модуля --source flask.

Ну и конечно, если говорить про автоматизацию, то должен быть написан тест, который "запустит" app.py, сделает вызов по main route 127.0.0.1:5000 проверит что ответ верен (200 статус там и т.п.) и отключится. Желательно, чтобы тест еще сделал несколько других действий типа дернул фальшивый url и проверил что 404 или отправил post туда где только get и проверил статус ошибки.

А после автор тестов насладится прекрасной статистикой покрытия проекта открыв страницу, содержащую покрытие автоматическими тестами. ;)

Вероятно, потому, что смешивая, мы нарушаем принцип поиска информации. В итоге ответ на вопрос "где искать" становится не очевидным.

Автор, спасибо за ссылку! Очень познавательно.

запоздала статья. Лет эдак на ...дцать.

В последней Django к формам можно крепить html в котором и кнопки есть и все что надо.

https://docs.djangoproject.com/en/5.0/ref/forms/api/#django.forms.Form

Кроме этого, все виджеты имеют свои шаблоны Можно переопределить шаблоны виджетов полей. https://docs.djangoproject.com/en/5.0/ref/forms/widgets/#widget

И напоследок. Я очень сочувствую всем, у кого настройка рендера формы происходит в __init__ а не непосредственно перед рендером на методе render. Как-никак на POST приходит запрос и надо только данные проверить, но у тех, кто все запихал в __init__, еще и инициализация рендера происходит. На 170 полях это становится больно. Да, я знаю, что в форме не должно быть столько полей.

да, пожалуйста. Посмотри ещё соседнюю статью про метаклассы, там тоже интересно и обсуждение активное.

Спасибо за поднятие темы еще раз в этом месяце. Могу отметить, что к пониманию, что такое метаклассы, так и не подобрались. И только в комментариях @Andrey_Solomatin наконец-то дал максимально близкое объяснение. Поблагодарил его отдельно.

Повторяя свой комментарий из соседней статьи. Метакласс это объект, который уточняет поведение какого либо другого класса, и метакласс, как и любой объект python, можно создать runtime (вопреки утверждению в статье):

MyMetaclass = type('MyMetaClass', (type,), {})

Управлять поведением класса и экземплярами класса можно несколькими способами: через декоратор, через наследование, через метакласс. Так в чем же разница?

  • Метакласс - официальный способ вклиниться в код ДО объявления класса. Это в статье отмечено, но не подчеркнуто, а ведь это единственное существенное отличие.

  • Декоратор класса: может донастраивать класс ПОСЛЕ объявления, поскольку получает на вход уже объявленный класс.

  • Родительский класс - уточняет поведение класса или экземпляров в runtime, предоставляя доступ к атрибутам и методам как напрямую так и через вызов super.

Именно хук beforeCreateClass и является тем важным инструментом метакласса, ради чего нужны метаклассы. И вот именно этот хук используется в ABC, хотя лучше бы использовать Protocol. Не реализовал метод протокола - лови Exception ДО создания класса на старте а не в runtime. Но это тоже шито белыми нитками из-за возможности объявления классов в runtime. Именно поэтому я категорически против Protocols/ABC, поскольку они способны добавить более сложные ошибки в код вместо упрощения.

Причина использования Metaclass в Django аналогична. На объявлении класса модели происходит внесение модели в реестр моделей (django.apps.apps.all_models). Последнее можно было бы решить более стандартно, например, через сигналы, но что сделано - то сделано.

В остальном по статье - утверждение "в Python все является объектом" намного глубже, чем может показаться на первый взгляд, и желаю автору добраться до настоящего понимания этого высказывания. Для этого, вероятно, стоит посмотреть повнимательнее внутрь cpython/Objects/typeobject.c

Может попробовать сократить свидания до одного в месяц и начать называть это бурной личной жизнью? Хотя... может как раз это у них "не так"?

Вы б добавили, что еще удалили всю команду отвечающую за Python, вместе Thomas Wouters, Release Manager for Python 3.12 and 3.13.
Thomas Wouters - who sits on the Python Steering Council, was fired as well. Читать обсуждение можно на HN.

А там на первом примере две одинаковых картинки и должны быть?

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

Я просто уже много лет докладываюсь на конференциях о том, что стоит прекратить писать на Python в Django-проектах, и стоит начать писать на Django и будет всем счастье. Все потому, что в 99% случаев в Django-проектах я вижу в способы написать код по-своему, хотя:

  1. В Django это уже есть. Достаточно сделать один import.

  2. Благодаря community это протестировано.

  3. Часто (не всегда) это в Django написано лучше или буквально так же. Последнее прям боль какая-то. DRY весь промок от слез.

Все, что мне остается, это сушить DRY и писать комментарии в стиле: RTFM! В смысле, посмотрите в Django, там уже это есть начиная с версии 0.9 :'(

а может просто таск и таймаут таймер завернуть в asyncio.wait(mytask, mytimeouttimer, return_when=asyncio.FIRST_COMPLETED)

мне кажется проще получится.

Автор. Просьба. Удали фильтр. Ну пожалуйста. Сделай метод post.body_as_markdown.

class Post(models.Model):

    def body_as_markdown(self):
        return mark_safe(markdown.markdown(self.body))  

В шаблоне получаем:

<div>
	{{ post.body_as_markdown }}
</div>

Я опускаю, что можно просто виджет поменять у поля. Как вариант еще можно отдельно сделать класс рендеринга в markdown или в xml в зависимости от контекста.

Тоже несколько раз перечитал, но похоже, что так и запланировано. Смотри получили запрос во view. Вызвали serializer, в котором почистили данные. Передали чистые данные в model, создали подключение в Utils, и с помощью helpers, вероятно, отправили в базу, и, если все успешно, вернулись опять во view, в котором отправляем email или message перед возвратом response.

Суть в том, что вся схема изначально, как и изобретение функтора во второй половине статьи говорит о том, что понимание Django у разработчиков описываемого кода не очень, собственно, как и Тестов.

И за такой базар могу ответить: переведи они тесты на DjangoTestCase, Django.SimpleTestCase - нытья про долгий старт быть не должно, как про суммарную скорость выполнения тестов. Хотя это им особо не важно, это не 43 минуты ожидания в четыре потока: на последнем проекте было, бесило страшно.

Назвать Unit.тестом тестирование нескольких sideeffects сразу язык не поворачивается, и беспокоит мысль: что ж за ка-ка-фония у них творится в реальной функции/методе?

По использованию django в статье все выглядит, что пользуются в основном drf, мучаются, но продолжают грызть юзать, опять же, без особого понимания: Там кое-где фильтры прям в коде проскакивали, аж больно смотреть.

Хотя чего я брюзжу. Если самолет у автора не падает, то значит он все правильно сделал. И любой критикан, как я, идет посрамленный, потому как у меня самолета нету. Даже игрушечного. :)

Цитируя документацию, ссылку на которую я привел в первом ответе:

setup() не имеет доступа к компоненту экземпляра — thisбудет иметь значение undefinedвнутри setup()

Инициализация объекта происходит непосредственно перед вызовом beforeCreate (Called immediately when the instance is initialized and props are resolved)

Это действительно может смущать, что есть какие-то переменные, которые объявляются до появления instance, и они станут атрибутами this после. Мы даже можем управлять видимостью этих переменных и решать, какие переменные в setup(), будут после отображены в instance, как атрибуты или методы. Ещё setup() может вернуть объекты явно, и такие объекты станут сразу отображены в глобальной зоне видимости всех элементов компонента, аналогично props-ам.

Да, спасибо. Стоило бы все же указать источник. Так вот, эта диаграмма подтверждает то, что компонента на этапе функции setup() не существует. В диаграмме даже отмечено цветом то, что функция Setup() (синий) не входит в список Component Lifecycle Hooks (красный), о которых рассказывается в текущей статье.

<zanuda mode="on">

В setup() компонента, как такового (тот самый this), еще не существует. Потому setup() не является событием жизни компонента. А статья именно про жизненный цикл, так что - все норм.

</zanuda>

Откуда это взялось? Или я_точно_синглетон = dict() тоже синглетон? Класс это экземпляр класса type или одного из его наследников, не более.

В принципе любой объект питона уникален, так что я_точно_синглетон_dict() можно тоже назвать Cинглетоном. Единый объект класса доступен каждому объекту через type(obj) или через дандер и часто используется как хранитель единого состояния для порожденных объектов, что, собственно, является одной из причин создания Singleton. Потому я субъективно отношу классы к Singleton-объектам.

Класс это экземпляр класса type или одного из его наследников, не более.

Нет. Класс не наследует свойства класса type, а является производной работы type. В качестве доказательства: по умолчанию у класса нет методов что есть у type, хотя их можно добавить.

type ничего не добавляет своим наследникам. Единственное что type.new делает, так это создаёт новый экземпляр переданного класса и заполняет его словарь, используя переданные параметры. Ну и валидацию выполняет, чтобы от некоторых типов не наследовались, но это не суть важно.

Еще раз. Класс не является наследником type, он является результатом работы функции конструктора класса. На примере:

 MyClass = type('Name', (__bases__,), {'mymethod': lambda *args, **kwargs: 'hi'})

В примере вызывается функция конструктор, создающая в локальной области видимости класс с именем "Name". Unbounded функции "mymethod" нет в методах базовых классов (если они переданы), и эта функция будет добавлена конструктором type к классу, да, грубо говоря добавлена в MyClass.__dict__, но если попробовать сделать это вручную получим'mappingproxy' object does not support item assignment. Так что это не напрямую "заполнение словаря".

Насколько я знаю, pathlib никаким образом не использует метаклассы. Поправьте, если я ошибаюсь.

Поправляю, Path наследован class PurePath(object), который, в свою очередь, ведет себя как metaclass, переопределяя __new__.

И даже если использует, то зачем в теории может понадобиться переопределять div на каком-нибудь PathType, а не на самом Path?

Переопределенный __div__ позволяет использовать синтаксис Path('root') / Path('folder'). В этом примере на команду __div__ происходит Path('root').parts + Path('folder').parts. Но Path в реальности это просто маска двух классов:cls = PureWindowsPath if os.name == 'nt' else PurePosixPath. Потому переопределив что-то у класса Path ничего не получим, надо переопределять у PureWindowsPath и PurePosixPath.

Но спасибо @ValeryIvanov кое что я узнал про конструктор в python3, от отличается от Python2, а я это упустил.

Мне понравилось, спасибо!

Вот только "С вероятностью в 20 % вы вернётесь к этому фрагменту и будете его дорабатывать" - а можно узнать как считалась вероятность? A то тут Мартин Фаулерс беспокоится, он то в статьях о рефакторинге совсем другие величины указал.

Мне кажется, что автор не до конца понимает слово "legacy" (наследие). Все. Абсолютно все. И что было написано, и то, что будет написано, это legacy проекта. Да да, даже то, что запланировано, но еще не реализовано.

Именно потому legacy не существует. Есть проект. В том состоянии, в каком он есть. Что-то устарело, что-то нет. На примере Django. Проекту множество лет. В проекте остаются следы переходов с одной технологии на другую, хотелки, не реализованные с 0.95 версии, куски разнотипного кода от разных авторов, и лютый Си-тон в некоторых папках. Legaсy? Нет. Просто фреймворк, с которым я работаю долгое время.

Я могу представить, что под понятием "Легаси" в статье спрятался "Технический долг". Набор технических решений, требующих вмешательства специалиста. Но надо ли с этим бороться?

Исходя из слов Мартина Фаулера о необходимости рефакторинга - в любом проекте есть Zero Tolerance Tech-Debt Zone, Михаель Феатерс уточнил, как эту зону обнаружить статистически. Всю остальную часть проекта стоит оставить неизменной.

Потому, не надо ни с чем бороться. Легаси, она в голове. А в проекте есть тех долг, только малая часть которого должна быть выплачена.

P.s. У меня либо паранойя, либо в тексте некоторые куски кода похожи на GPT. Подозреваю первое.

"Ничего ты не знаешь, Джон Сноу"

Если рассматривать статью, как попытку разобраться в метаклассах. То зачет. Попытался.

Если рассматривать статью, как попытку разобраться в метаклассах. То мимо. Не разобрался.

Просто и понятно написано тут https://docs.python.org/3/reference/datamodel.html#metaclasses, но обычно это никто не читает.

  1. Объекты класса как-то себя "проявляют" через методы и содержат атрибуты. Получить класс объекта можно командой type(obj).

  2. Классы задают базовое поведение и задают шаблоны атрибутов для объектов класса. Любой класс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить метакласс класса можно командой type(cls). Именно потому, что класс это тоже объект.

  3. Метаклассы задают базовое поведение и задают шаблоны атрибутов для Классов, как объектов. Любой метакласс это синглтон-объект, который как-то себя "проявляет" через методы и содержит атрибуты. Получить Метакласс метакласса можно командой type(metacls). Именно потому, что метакласс - это тоже объект.

  4. Можно продолжить. Создав мета мета класс, который будет задавать базовое поведение и задают шаблоны атрибутов для Мета классов, как объектов.

Фактически у нас есть только одно взаимоотношение. Класс --> Объект.

В списке выше это 2 --> 1, 3 --> 2, 4 --> 3, ...

Метакласс MyMetaв примере статьи, например, управляет поведением инициализации объекта классом MyClass.

Первый пример применения метакласса: фактически "Мета классом" мы "донастраиваем" работу некоторых методов класса. В Django метакласc модели превращает атрибуты-поля Класса Модели в атрибуты-дескрипторы объекта на моменте инициализации классом объекта. Если бы этого не происходило, то в объекте мы бы имели атрибут-поле (models.RelatedField). Но в объекте мы имеем models.DeferredDescriptor.

Метакласс имеет не так много методов, поскольку не так уж и много нам надо делать на этом уровне. Базовый метакласс, это который type, например, при создании любого класса добавляет ему набор стандартных дандер-методов, типа __getattr__, и т.п. В твоем примере ты создал class MyClass() ничего ему не объявил, а у класса есть метод __str__. Спасибо метаклассу type который по умолчанию является конструктором класса и делает всю грязную работу за нас.

Это может пригодиться, если хочется поменять некоторые атодобавляемые методы у ВСЕХ классов этого метакласса. Опять Django. Добавляемый метод __eq__ у класса модели переопределен. Теперь в классе это поведение поменяно. И объекты этого класса в момент сравнения начинают меряться obj.pk ? obj2.pk вместо стандартного поведения id(obj) ? id(obj2)

Вот второй пример применения метакласса: вместо ручного прописывания методов у каждого дочернего класса, делаем это через метакласс. Кстати, я предпочитаю использовать для этого миксины. В dataclass - это же делают через декоратор.

Поскольку Метакласс позволяет переопределять дандер и не только методы на момент объявления класса (это же возможно сделать и миксином и наследованием и декоратором) то можно переопределить "__div__" и всю остальную математику и получить класс Path из Pathlib который реализует DSL, в данном случае это привычный file system syntax. Но важно, что этот класс на самом деле - это несколько классов PosixPath, WindowsPath etc. Без метакласса надо было бы переопределять математику в каждом из них ручками. Количество кода увеличилось бы. А через метакласс удалось соблюсти DRY да и KISS тоже где-то рядом.

По сути метакласс - это всегда синтаксический сахар сильно уменьшающий количество кода, который надо писать. Тут нет магии или мистики. Это просто. Но не для всех.

Верно. Но этот Exception в коде никак не обрабатывается. Я это имел ввиду под "не работает".

Information

Rating
3,102-nd
Location
Zams, Tirol, Австрия
Date of birth
Registered
Activity

Specialization

Backend Developer, Fullstack Developer
Lead
From 8,000 €
Python
Django
Ajax
OOP
Design patterns
Vue.js
JavaScript
HTML
CSS