Успешный
Open Source
Андрей Светлов
О себе
● 15 лет использования Python
● Python Core Developer
● Соавтор нескольких библиотек (asyncio,
aiohttp, aiozmq, aio...
Код на github
Этого явно недостаточно
Публичность
● Не знают — не используют.
● Статьи, блоги, stackoverflow и т.д.
● Не жалейте время на bug tracker
Документация
● Шанс составить первое впечатление
● Английский
● Docstrings и README.rst — недостаточно
● Нарративность
● П...
Тесты
● Читаемые и хорошо организованные
● Полные
● coverage.py
● Как минимум 95% покрытие
● Continuous Integration (travi...
Код
● pep8 — обязательно
● VCS (гитхаб прекрасен)
● Версии продукта
● Ветки, тэги, релизы
● Примеры использования
Инфраструктура
● setup.py
● PyPI
● readthedocs
● travis-ci
● инструкция по установке
Ясность
Хорошая библиотека — простая библиотека
Pubic и Private API
class A:
def __init__(self, param):
self.param = param
def public(self):
return self.private()
def pri...
Public API
● по умолчанию всё "закрыть".
● открывать только то что сознательно
желаем сделать открытым
● Не нужно бояться ...
Docstrings
class A:
def __init__(self, param):
self._param = param
def public(self):
return self._private()
def _private(s...
Понятное API
Если нелегко написать документацию — API
плохое
Хорошие имена
twisted.internet.defer.
Deferred
Twised's Deferred
does something —
OMG!
asyncio.Future
Трудность выбора имен
ZeroMQ Req/Rep, Push/Pull и Pub/Sub
Requester? Pusher???
RPC Извещения Подписка
Клиент Req Push Pub
...
Решение
connect_rpc — RPCClient
serve_rpc — RPCServer
connect_pipeline — PipelineClient
serve_pipeline — PipelineServer
co...
Наследование
class Base:
def do_work(self):
self.handle('param')
class Derived(Base):
def handle(self, param):
do_somethin...
Агрегация лучше наследования
class Service:
def do_work(self):
self.log('operaion')
def log(self, param):
pass
class Socke...
Естественная полнота API
class Subscriber:
def subscribe(self, name):
pass
class Subscriber:
def subscribe(self, name):
pa...
Предметная область
Матрицы
a * b
a @ b
a + b
len(a)
Устоявшиеся соглашения
class Point:
def __init__(self, a, b):
self.a, self.b = a, b
def __add__(self, other):
self.a += ot...
Магические методы
def __eq__(self, other):
if isinstance(other, Point):
return self._x == other._x and self._y == other._y...
Коллекции
class ReadonlyDict(collections.abc.Mapping):
def __init__(self, dct):
self._dct = dct
def __len__(self):
return ...
Неудачный пример
sqlalchemy.engine.result.RowProxy
Это, хмм, abc.Sequence
len(result_proxy)
'column_name' in result_proxy
...
Исключения
def login(login, passwd):
res = db.execute(
"SELECT * FROM users "
"WHERE login = %s", login)
return res['passw...
Singletons
Просто зло.
Я их не использую.
Логирование
logging.getLogger('').info('log message')
Настраивание объема сообщений в лог
production != dev environment
би...
Проверка параметров
def write(self, data):
if not isinstance(data, (bytes, bytearray, memoryview)):
raise TypeError('data ...
библиотека != приложение
● Обратная совместимость
● Внимание к деталям
● Простота и понятность
● Документация
Вопросы?
andrew.svetlov@gmail.com
Writing Open Source Library
Upcoming SlideShare
Loading in …5
×

Writing Open Source Library

924 views

Published on

My slides from pycon.ru 2014

Published in: Software
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
924
On SlideShare
0
From Embeds
0
Number of Embeds
252
Actions
Shares
0
Downloads
7
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Writing Open Source Library

  1. 1. Успешный Open Source Андрей Светлов
  2. 2. О себе ● 15 лет использования Python ● Python Core Developer ● Соавтор нескольких библиотек (asyncio, aiohttp, aiozmq, aiopg) ● andrew.svetlov@gmail.com ● http://asvetlov.blogspot.com
  3. 3. Код на github Этого явно недостаточно
  4. 4. Публичность ● Не знают — не используют. ● Статьи, блоги, stackoverflow и т.д. ● Не жалейте время на bug tracker
  5. 5. Документация ● Шанс составить первое впечатление ● Английский ● Docstrings и README.rst — недостаточно ● Нарративность ● Примеры ● readthedocs.org
  6. 6. Тесты ● Читаемые и хорошо организованные ● Полные ● coverage.py ● Как минимум 95% покрытие ● Continuous Integration (travis-ci.org)
  7. 7. Код ● pep8 — обязательно ● VCS (гитхаб прекрасен) ● Версии продукта ● Ветки, тэги, релизы ● Примеры использования
  8. 8. Инфраструктура ● setup.py ● PyPI ● readthedocs ● travis-ci ● инструкция по установке
  9. 9. Ясность Хорошая библиотека — простая библиотека
  10. 10. Pubic и Private API class A: def __init__(self, param): self.param = param def public(self): return self.private() def private(self): return self.param * 2 class A: def __init__(self, param): self._param = param def public(self): return self._private() def _private(self): return self._param * 2
  11. 11. Public API ● по умолчанию всё "закрыть". ● открывать только то что сознательно желаем сделать открытым ● Не нужно бояться "закрытых" классов
  12. 12. Docstrings class A: def __init__(self, param): self._param = param def public(self): return self._private() def _private(self): """Private function""" return self._param * 2 class A: """Docstring for class""" def __init__(self, param): self._param = param def public(self): """This is public""" return self._private() def _private(self): # Private function return self._param * 2
  13. 13. Понятное API Если нелегко написать документацию — API плохое
  14. 14. Хорошие имена twisted.internet.defer. Deferred Twised's Deferred does something — OMG! asyncio.Future
  15. 15. Трудность выбора имен ZeroMQ Req/Rep, Push/Pull и Pub/Sub Requester? Pusher??? RPC Извещения Подписка Клиент Req Push Pub Сервер Rep Pull Sub
  16. 16. Решение connect_rpc — RPCClient serve_rpc — RPCServer connect_pipeline — PipelineClient serve_pipeline — PipelineServer connect_pubsub — PubSubClient serve_pubsub — PubSubServer
  17. 17. Наследование class Base: def do_work(self): self.handle('param') class Derived(Base): def handle(self, param): do_something() class Base(metaclass=abc.ABCMeta): def do_work(self): self.handle('param') @abc.abstractmethod def handle(self, param): pass class Derived(Base): def handle(self, param): do_something()
  18. 18. Агрегация лучше наследования class Service: def do_work(self): self.log('operaion') def log(self, param): pass class SocketLoggerMixin: def log(self, param): self.socket.send(param) class SocketLoggingService( SocketLoggerMixin, Service): pass class Service: def __init__(self, logger): self._logger = logger def do_work(self): self._logger.log('operaion') class Logger: def log(self, param): pass
  19. 19. Естественная полнота API class Subscriber: def subscribe(self, name): pass class Subscriber: def subscribe(self, name): pass def unsubscribe(self, name): pass @property def subscriptions(sef): pass
  20. 20. Предметная область Матрицы a * b a @ b a + b len(a)
  21. 21. Устоявшиеся соглашения class Point: def __init__(self, a, b): self.a, self.b = a, b def __add__(self, other): self.a += other.a self.b += other.b return self class Point: def __init__(self, x, y): self._x, self._y = x, y x = property(lambda self: self._x) y = property(lambda self: self._y) def __add__(self, other): return Point(self._x + other. _x, self._y + other. _y)
  22. 22. Магические методы def __eq__(self, other): if isinstance(other, Point): return self._x == other._x and self._y == other._y else: return NotImplented def __ne__(self, other): return not self == other def __hash__(self): return (self._x, self._y)
  23. 23. Коллекции class ReadonlyDict(collections.abc.Mapping): def __init__(self, dct): self._dct = dct def __len__(self): return len(self._dct) def __iter__(self): return iter(self._dct) def __getitem__(self, key): return self._dct[key] assert set(ReadonlyDict({'a': 1, 'b': 2}).keys()) == set('a', 'b')
  24. 24. Неудачный пример sqlalchemy.engine.result.RowProxy Это, хмм, abc.Sequence len(result_proxy) 'column_name' in result_proxy result_proxy[0] result_proxy['column_name'] result_proxy[1: 5] result_proxy1 < result_proxy2 iter(result_proxy)
  25. 25. Исключения def login(login, passwd): res = db.execute( "SELECT * FROM users " "WHERE login = %s", login) return res['passwd'] == passwd def login(login, passwd): try: res = db.execute( "SELECT * FROM users " "WHERE login = %s", login) if res['passwd'] != passwd: raise CannotLogin() except DatabaseNotFoundError as ex: raise CannotLogin() from ex
  26. 26. Singletons Просто зло. Я их не использую.
  27. 27. Логирование logging.getLogger('').info('log message') Настраивание объема сообщений в лог production != dev environment библиотека != приложение пользователя
  28. 28. Проверка параметров def write(self, data): if not isinstance(data, (bytes, bytearray, memoryview)): raise TypeError('data argument must be byte-ish (%r)', type(data)) if server_side: if not sslcontext: raise ValueError('Server side ssl needs a valid SSLContext')
  29. 29. библиотека != приложение ● Обратная совместимость ● Внимание к деталям ● Простота и понятность ● Документация
  30. 30. Вопросы? andrew.svetlov@gmail.com

×