Designing a Pythonic Interface @honzakral Illustrated guide to - - PowerPoint PPT Presentation

designing a pythonic interface
SMART_READER_LITE
LIVE PREVIEW

Designing a Pythonic Interface @honzakral Illustrated guide to - - PowerPoint PPT Presentation

Designing a Pythonic Interface @honzakral Illustrated guide to this import this Disclaimer Personal opinions Do as I say, not as I do API API is a service for "code" Fulfills a "contract" Vaguer Simplifying Access


slide-1
SLIDE 1

Designing a Pythonic Interface

@honzakral

slide-2
SLIDE 2

Illustrated guide to this

slide-3
SLIDE 3
slide-4
SLIDE 4

import this

slide-5
SLIDE 5

Disclaimer

Personal opinions Do as I say, not as I do

slide-6
SLIDE 6

API

slide-7
SLIDE 7

API is a service for "code"

slide-8
SLIDE 8

Fulfills a "contract"

slide-9
SLIDE 9

Vaguer

slide-10
SLIDE 10

Simplifying Access

Simple is better than complex. Complex is better than complicated.

slide-11
SLIDE 11

Hiding complexity

response = client.search( index="my-index", body={ "query": { "bool": { "must": [{"match": {"title": "python"}}], "must_not": [{"match": {"description": "beta"}}] "filter": [{"term": {"category": "search"}}] } }, "aggs" : { "per_tag": { "terms": {"field": "tags"}, "aggs": { "max_lines": {"max": {"field": "lines"}} } } } } ) for hit in response['hits']['hits']: print(hit['_score'], hit['_source']['title'])

slide-12
SLIDE 12

Hiding complexity

s = Search(using=client, index="my-index") s = s.filter("term", category="search") s = s.query("match", title="python") s = s.query(~Q("match", description="beta")) s.aggs.bucket('per_tag', 'terms', field='tags') \ .metric('max_lines', 'max', field='lines') for hit in s: print(hit.meta.score, hit.title)

slide-13
SLIDE 13

Be explicit!

Explicit is better than implicit.

slide-14
SLIDE 14

Hide mechanics,

not meaning!

slide-15
SLIDE 15

Mechanics

response = client.search( index="my-index", body={ "query": { "bool": { "must": [{"match": {"title": "python"}}], "must_not": [{"match": {"description": "beta"}}] "filter": [{"term": {"category": "search"}}] } }, "aggs" : { "per_tag": { "terms": {"field": "tags"}, "aggs": { "max_lines": {"max": {"field": "lines"}} } } } } ) for hit in response['hits']['hits']: print(hit['_score'], hit['_source']['title'])

slide-16
SLIDE 16

s = Search(using=client, index="my-index") s = s.filter("term", category="search") s = s.query("match", title="python") s = s.query(~Q("match", description="beta")) s.aggs.bucket('per_tag', 'terms', field='tags') \ .metric('max_lines', 'max', field='lines') for hit in s: print(hit.meta.score, hit.title)

Meaning

slide-17
SLIDE 17

s = Search(using=client, index="my-index") s = s.filter("term", category="search") s = s.query("match", title="python") s = s.query(~Q("match", description="beta")) s.aggs.bucket('per_tag', 'terms', field='tags') \ .metric('max_lines', 'max', field='lines') response = client.search(index="my-index", body=s.to_dict())

Admit to leakiness

slide-18
SLIDE 18

Be familiar!

In the face of ambiguity, refuse the temptation to guess.

slide-19
SLIDE 19

Copy shamelessly

q = Entry.objects.filter(headline__startswith="What") q = q.exclude(pub_date__gte=date.today()) q = q.filter(pub_date__gte=date.today()) curl -XGET localhost:9200/my-index/_search -d '{ "query": { "bool": { "must": [{"match": {"title": "python"}}], "must_not": [{"match": {"description": "beta"}}] "filter": [{"term": {"category": "search"}}] } }, "aggs" : { "per_tag": { "terms": {"field": "tags"}, "aggs": { "max_lines": {"max": {"field": "lines"}} } } } }'

slide-20
SLIDE 20

Be consistent!

Special cases aren't special enough to break the rules.

slide-21
SLIDE 21

If it makes sense

Although practicality beats purity.

slide-22
SLIDE 22

s = Search(using=client, index="my-index") s = s.filter("term", category="search") s = s.query("match", title="python") s = s.query(~Q("match", description="beta")) s.aggs.bucket('per_tag', 'terms', field='tags') \ .metric('max_lines', 'max', field='lines')

slide-23
SLIDE 23

Be friendly!

slide-24
SLIDE 24

Python is interactive

slide-25
SLIDE 25

dir, __repr__, __doc__

>>> for hit in Search().query("match", title="pycon"): ... dir(hit) ... ["meta", "title", "body", ...] >>> >>> >>> Q({ ... "bool": { ... "must": [{"match": {"title": "python"}}], ... "must_not": [{"match": {"description": "beta"}}] ... } ... }) Bool(must=[Match(title='python')], must_not=[Match(description='beta')]) >>> >>> >>> help(Search.to_dict) Help on function to_dict in module elasticsearch_dsl.search: to_dict(self, count=False, **kwargs) Serialize the search into the dictionary that will be sent over as the requests body. :arg count: a flag to specify we are interested in a body for count - no aggregations, no pagination bounds etc. All additional keyword arguments will be included into the dictionary.

slide-26
SLIDE 26

Iterative build

Flat is better than nested. Sparse is better than dense.

slide-27
SLIDE 27

Iterative build

s = Search(using=client, index="my-index") # filter only search s = s.filter("term", category="search") # we want python in title s = s.query("match", title="python") # and no beta releases s = s.query(~Q("match", description="beta")) # aggregate on tags s.aggs.bucket('per_tag', 'terms', field='tags') # max lines per tag s.aggs['per_tag'].metric('max_lines', 'max', field='lines')

slide-28
SLIDE 28

Safety is friendly

Errors should never pass silently. Unless explicitly silenced.

slide-29
SLIDE 29

Fail by default

allow ignore

slide-30
SLIDE 30

Tests? Tests!

slide-31
SLIDE 31

Be flexible!

API is still code

slide-32
SLIDE 32

Things change

Adapt!

slide-33
SLIDE 33

No code is perfect

Now is better than never. Although never is often better than *right* now.

slide-34
SLIDE 34

Thanks!

@honzakral