GeezerVisonThe World Wide Web, restored to 800 pixels of glory. |
| Home|About|Gaming cabinet|Archives|How it works|Starter sites |
GeezerVison is showing github.com — dry-python.github.io/slides/introducing-dry-python.pug at develop · dry-python/dry-python.github.io Expand file tree/ Copy pathintroducing-dry-python.pugMore file actions 416 lines (399 loc) · 15.1 KB / Copy pathintroducing-dry-python.pugFile metadata and controls416 lines (399 loc) · 15.1 KB 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 doctype html html head meta(charset="utf-8") meta(name="description" content="Introducing dry-python") meta(name="author" content="Artem Malyshev") meta(name="apple-mobile-web-app-capable" content="yes") meta(name="apple-mobile-web-app-status-bar-style" content="black-translucent") meta(name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no") title Introducing dry-python body .reveal .slides section h2 Introducing h1 a(href="https://dry-python.org/"). dry-python h4 Artem Malyshev section h2 BIO ul li Co-Founder at #[a(href="https://drylabs.io/") drylabs.io] li #[a(href="https://dry-python.org/") dry-python.org] li Django Channels 1.0 li 5 years of experience in Python section h2 Code is... p.fragment b hard p.fragment b and frustrating p.fragment Let's consider we're developing subscription button for a web service section(data-background-image=require("./pics/startup.jpg").default data-background-size="contain") h2(style="color: white; background-color: black") Startup br br br br br br br br section h2 Micro framework ol li.fragment Long handlers li.fragment Lots of "if" statements section h2 Long handlers pre code.python. 85 @app.route('/subscriptions/') 86 def buy_subscription(page): ... 121 if props[-1].endswith('$'): 122 -#{">"} props[-1] = props[-1][:-1] 123 pre.fragment code.python. Traceback (most recent call last): File "views.py", line 1027, in buy_subscription ZeroDivisionError: division by zero section(data-background-image=require("./pics/enterprise.jpg").default data-background-size="contain") h2(style="color: white; background-color: black") Enterprise br br br br br br br br section h2 Big framework ol li.fragment You need method flowchart li.fragment Zig-zag in the traceback li.fragment Framework internals leak section h2 Implicit API pre code.python. class SubscriptionViewSet(viewsets.ModelViewSet): queryset = Subscription.objects.all() serializer_class = SubscriptionSerializer permission_classes = (CanSubscribe,) filter_class = SubscriptionFilter ol li.fragment What exactly does this class do? li.fragment How to use it? section(data-background-image=require("./pics/method-flowchart.png").default data-background-size="contain") br section h2 Framework internals leak pre code.python. class SubscriptionSerializer(Serializer): category_id = IntegerField() price_id = IntegerField() pre.fragment code.python. def recreate_nested_writable_fields(self, instance): for field, values in self.writable_fields_to_recreate(): related_manager = getattr(instance, field) related_manager.all().delete() for data in values: obj = related_manager.model.objects.create( to=instance, **data) related_manager.add(obj) section h2 As a result code is... ol li.fragment Fragile li.fragment Hard to reason about li.fragment Time-consuming section img(src=require("./pics/crazy-telephone-wires.png").default) section blockquote p If your code is crap, stickies on the wall won't help. a(href="https://twitter.com/henrikkniberg"). @HenrikKniberg img(src=require("./pics/stickers-on-the-wall.jpg").default) section h2 Service layer p Defines an application's boundary with a layer of services that establishes a set of available operations and coordinates the application's response in each operation. p b by Randy Stafford section h2 Business objects pre code.python. def buy_subscription(category_id, price_id, user): category = find_category(category_id) price = find_price(price_id) profile = find_profile(user) if profile.balance #{"<"} price.cost: raise ValueError decrease_balance(profile, price.cost) save_profile(profile) expires = calculate_period(price.period) subscription = create_subscription( profile, category, expires) notification = send_notification( 'subscription', profile, category.name) section h2 Business object problems ol li.fragment Mixed state, implementation and specification li.fragment Growth problem li.fragment Top-down architecture section img(src="https://raw.githubusercontent.com/dry-python/brand/master/logo/dry-python.png").plain p A set of libraries for pluggable business logic components. p Answers how we decompose and organize business logic. section img(src="https://raw.githubusercontent.com/dry-python/brand/master/logo/stories.png").plain p Define a user story in the business transaction DSL. p Separate state, implementation and specification. section h2 DSL pre code.python. from stories import story, arguments class Subscription: @story @arguments('category_id', 'price_id') def buy(I): I.find_category I.find_price I.find_profile I.check_balance I.persist_payment I.persist_subscription I.send_subscription_notification section h2 Context pre code.python. (Pdb) ctx Subscription.buy: find_category check_price check_purchase (PromoCode.validate) find_code (skipped) check_balance find_profile Context: category_id = 1318 # Story argument user = #{"<"}User: 3292#{">"} # Story argument category = #{"<"}Category: 1318#{">"} # Set by Subscription.find_category section(data-background-image=require("./pics/debug-toolbar.png").default data-background-size="contain") h2 DEBUG TOOLBAR br br br br br br br br br br section(data-background-image=require("./pics/pytest.png").default data-background-size="contain") h2(style="color: white") py.test section(data-background-image=require("./pics/sentry.png").default data-background-size="contain") h2 Sentry section h2 Usage ol li.fragment Story decorator build an execution plan pre code.python. class Subscription: @story def buy(I): I.find_category li.fragment Execute object methods according to plan pre code.python. def find_category(self, ctx): category = Category.objects.get( pk=ctx.category_id) return Success(category=category) li.fragment We call the story method pre code.python. subs = Subscription() subs.buy(category_id=1, price_id=1) section h2 Failures ol li.fragment Define a number of reasons pre code.python. @Subscription.buy.failures class Errors(Enum): low_balance = auto() li.fragment Failure will stop the execution of the whole story pre code.python. def check_balance(self, ctx): if ctx.profile.balance #{"<"} ctx.price.cost: return Failure(Errors.low_balance) else: return Success() li.fragment We check failure reason pre code.python. result = Subscription().buy.run(category_id=2) assert result.failed_because(Errors.low_balance) section h2 Contract ol li.fragment Define a number of variable validators pre code.python. from pydantic import BaseModel @Subscription.buy.contract class Context(BaseModel): user: User category_id: int category: Optional[Category] li.fragment Return variables from step pre code.python. def find_category(self, ctx: "Context"): category = get_category( ctx.category_id) return Success(category=category) section h2 Substories ol li.fragment Steps can be stories as well pre code.python. class Subscription: @story def buy(I): I.calculate_discount I.check_balance @story def calculate_discount(I): I.find_promo_code I.check_code_expiration li.fragment Each step can stop the execution of current substory pre code.python. def check_code_expiration(self, ctx): if ctx.promo_code.is_expired(): return Skip() else: return Success() section img(src="https://raw.githubusercontent.com/dry-python/brand/master/logo/dependencies.png").plain p Provide composition instead of inheritance. p Solves top-down approach problem. section h2 Delegate responsibility pre code.python. class Subscription: def find_category(self, ctx): category = self.load_category(ctx.category_id) return Success(category=category) def find_price(self, ctx): price = self.load_price(ctx.price_id) return Success(price=price) def __init__(self, load_category, load_price): self.load_category = load_category self.load_price = load_price section h2 Injection pre code.python. from dependencies import Injector, Package app = Package('app') class BuySubscription(Injector): buy_subscription = app.services.Subscription.buy load_category = app.repositories.load_category load_price = app.repositories.load_price load_profile = app.repositories.load_profile BuySubscription.buy_subscription(category_id=1, price_id=1) section h2 Django views pre code.python. from dependencies import operation from dependencies.contrib.django import view from django.http import HttpResponse, HttpResponseRedirect @view class BuySubscriptionView(BuySubscription): @operation def post(buy_subscription, category_id, price_id): result = buy_subscription.run(category_id, price_id) if result.is_success: return HttpResponseRedirect(to=result.value) elif result.failed_on('check_balance'): return HttpResponse('#{"<"}h1#{">"}Not enough money#{"<"}/h1#{">"}') section h2 Flask views pre code.python. from dependencies import operation from dependencies.contrib.flask import method_view from flask import redirect @method_view class BuySubscriptionView(BuySubscription): @operation def post(buy_subscription, category_id, price_id): result = buy_subscription.run(category_id, price_id) if result.is_success: return redirect(result.value) elif result.failed_on('check_balance'): return '#{"<"}h1#{">"}Not enough money#{"<"}/h1#{">"}' section h2 Celery Tasks pre code.python. from dependencies import operation from dependencies.contrib.celery import task @task class PutMoneyTask(PutMoney): @operation def run(put_money, user, amount, task): result = put_money.run(user, amount) if result.is_failure: task.on_failure(result.ctx.transaction_id) section h2 Plans ol li Delegates li Rollbacks li asyncio support li pyramid support li typing advantages li linters integration li language server section h2 Try it! pre code. $ pip install stories pre code. $ pip install dependencies section h2 Get in touch ul li a(href="https://dry-python.org/") dry-python.org li a(href="https://twitter.com/dry_py") twitter.com/dry_py li a(href="https://github.com/dry-python") github.com/dry-python li a(href="https://gitter.im/dry-python") gitter.im/dry-python |
| You are visitor 01402503 — GeezerVison does not store the pages you look up. Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required. |