-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
587 lines (451 loc) · 16.9 KB
/
index.html
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
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
<!DOCTYPE html>
<html>
<head>
<title>No tot és programar!</title>
<meta charset="utf-8">
<style>
@import url(https://fonts.googleapis.com/css?family=Yanone+Kaffeesatz);
@import url(https://fonts.googleapis.com/css?family=Roboto);
@import url(https://fonts.googleapis.com/css?family=Roboto+Mono);
body { font-family: 'Roboto'; }
h1, h2, h3 {
font-family: 'Yanone Kaffeesatz';
font-weight: normal;
}
.remark-code, .remark-inline-code { font-family: 'Roboto Mono'; font-size: 0.6em }
.small {
font-size: 0.8em;
}
a:link, a:visited {
color: #E40066;
text-decoration: underline;
}
</style>
</head>
<body>
<textarea id="source">
class: center, middle
count: false
# No tot és programar!
## Documentar, testejar, integrar, desplegar i monitoritzar
![PyGRN](https://avatars1.githubusercontent.com/u/28831124?v=4&s=200)
[Eduard Carreras i Nadal](mailto:[email protected])
13/09/2017 - PyGRN #2
[![](https://i.creativecommons.org/l/by-sa/4.0/88x31.png)](http://creativecommons.org/licenses/by-sa/4.0/)
---
# Qui sóc jo?
- Sóc un infiltrat en el món del software (sóc teleco)
- Apassionat del software i del proćes **art**esanal.
- Co-Fundador de [GISCE-TI, S.L](http://gisce.net)
- Desenvolupant en Python des del 2007 (Python ❤️)
- Amant del programari lliure
- [LinkedIn](https://www.linkedin.com/in/ecarreras/), [Twitter](https://twitter.com/ecarreras)
i [GitHub](https://github.com/ecarreras)
---
# Agenda
1. Documentació de projectes
2. Testing
3. Integració contínua
4. Desplegament
5. Monitorització
???
Separaré la xerrada en aquests blocs
Té a veure amb el cicle que m'agradaria
---
# Documentació de projectes
- Documentació tècnica (APIs)
- Documentació d'usuari (manuals)
---
## Documentació tècnica (APIs)
- [PEP 257 -- Docstring Conventions](https://www.python.org/dev/peps/pep-0257/)
A docstring is a string literal that occurs as the first statement in a module,
function, class, or method definition. Such a docstring becomes the `__doc__`
special attribute of that object.
```python
"""Aquest mòdul de python té funcions per saludar"""
def hola():
"""Això imprimeix Hola!"""
print('Hola!')
```
- Podem consultar l'ajuda des de l'entorn python `help(hola)`
o `hola.__doc__`
- Ens hauríem d'acostumar més a escriure *docstrings* com a documentació tècnica.
No només les signatures de les funcions.
---
## Documentació tècnica (APIs) - Història
- No hi havia cap estàndard definit.
- Què passa quan es vol *enriquir* una mica el text?
- HTML, DocBook, TeX, ...
### Objectius
- Ha de ser fàcil de llegir des del propi codi font
- Fàcil d'escriure des de qualsevol editor
- No ha de tenir informació que hagi de ser deduïda de parsejar el mòdul
- Ha de tenir prou *estructura* per poder-se convertir a altres formats
- Ha de ser fàcil escriure **tota** la documentació d'un mòdul còmodament
### Solució
- [PEP 287 -- reStructuredText Docstring Format](https://www.python.org/dev/peps/pep-0287/)
---
## Documentació tècnica (APIs) - ReStructuredText exemple
```python
class Keeper(Storer):
"""Keep data fresher longer.
Extend `Storer`. Class attribute `instances` keeps track
of the number of `Keeper` objects instantiated.
"""
instances = 0
"""How many `Keeper` objects are there?"""
def __init__(self):
"""
Extend `Storer.__init__()` to keep track of
instances. Keep count in `self.instances` and data
in `self.data`.
"""
Storer.__init__(self)
self.instances += 1
self.data = []
"""Store data in a list, most recent last."""
def storedata(self, data):
"""
Extend `Storer.storedata()`; append new `data` to a
list (in `self.data`).
"""
self.data = data
```
---
## Documentació tècnica (APIs) - Metainformació
- Diferents formats de metainformació
- ReStructuredText
- [Google](http://google.github.io/styleguide/pyguide.html?showone=Comments#Comments)
- [Numpy](https://github.com/numpy/numpy/blob/master/doc/HOWTO_DOCUMENT.rst.txt)
- [PEP 3107 -- Function Annotations](https://www.python.org/dev/peps/pep-3107/)
(només Python 3!)
### ReStructuredText
```python
def stupid_method(first, second):
"""Aquest mètode retorna la suma dels dos paràmetres.
:param int first: El primer paràmentre
:param int second: El segon paràmentre
:return: La suma dels paràmetres
:rtype: int
:raises ValueError: Si algun dels dos paràmetres no és un enter
"""
if not (isinstance(first, int) and isinstance(second int)):
raise ValueError("must be integers!")
return first + second
```
---
## Documentació tècnica (APIs) - Metainformació
### Google
```python
def stupid_method(first, second):
"""Aquest mètode retorna la suma dels dos paràmetres.
Args:
first: Un int com a primer paràmetre.
second: Un int com a segon paràmetre.
Returns:
Un int corresponent a la suma dels paràmetres
Raises:
ValueError: Si algun dels dos paràmetres no és un enter
"""
if not (isinstance(first, int) and isinstance(second, int)):
raise ValueError("must be integers!")
return first + second
```
---
## Documentació tècnica (APIs) - Metainformació
### Numpy
```python
def stupid_method(first, second):
"""Aquest mètode retorna la suma dels dos paràmetres.
Parametres
----------
first: int
Un int com a primer paràmetre.
second: int
Un int com a segon paràmetre.
Returns
-------
int
La suma dels paràmetres
Raises
------
ValueError
Si algun dels dos paràmetres no és un enter
"""
if not (isinstance(first, int) and isinstance(second, int)):
raise ValueError("must be integers!")
return first + second
```
---
## Documentació tècnica (APIs) - Metainformació
### Function Annotations
```python
def stupid_method(first: int, second: int) -> int:
"""Aquest mètode retorna la suma dels dos paràmetres.
:param first: El primer paràmentre
:param second: El segon paràmentre
:return: La suma dels paràmetres
:raises ValueError: Si algun dels dos paràmetres no és un enter
"""
if not (isinstance(first, int) and isinstance(second, int)):
raise ValueError("must be integers!")
return first + second
```
```python
def stupid_method(first: "El primer paràmentre",
second: "El segon paràmentre") -> "La suma dels paràmetres":
"""Aquest mètode retorna la suma dels dos paràmetres.
:raises ValueError: Si algun dels dos paràmetres no és un enter
"""
if not (isinstance(first, int) and isinstance(second, int)):
raise ValueError("must be integers!")
return first + second
```
- Podem accedir a la informació des de l'intèrpret. `supid_method.func_annotations`
i retorna un diccionari amb les anotacions.
---
## Documentació tècnica (APIs) - Doctest
- Barreja entre tenir documentació, exemples i tests. (3 en 1).
- La [documentació de doctest](https://docs.python.org/3/library/doctest.html)
és molt extensa i permet fer moltes coses.
```python
def stupid_method(first, second):
"""Aquest mètode retorna la suma dels dos paràmetres.
>>> stupid_method(1, 2)
3
>>> stupid_method("a", 2)
Traceback (most recent call last):
...
ValueError: must be integers!
>>> stupid_method(1, "a")
Traceback (most recent call last):
...
ValueError: must be integers!
"""
if not (isinstance(first, int) and isinstance(second, int)):
raise ValueError("must be integers!")
return first + second
if __name__ == '__main__':
import doctest
doctest.testmod()
```
- Podem llançar els tests directament amb:
```sh
$ python stupid.py -v
```
---
## Documentació tècnica (APIs) - Eina
### [Sphinx](http://www.sphinx-doc.org)
És una eina per **crear documentació** i podem destacar les següents funcionalitats:
- **Diferents formats de sortida**: HTML (diferents temes), WinHelp,
LaTeX (pels PDFs), ePub, Texinfo, manpages, text pla.
- **Referències creuades**: Enllaços semàntics automàtics de funcions, classes,
definicions, ... Fins i tot d'altres documentacions!
- **Estructura jeràrquica**: Tot queda organitzat en arbre.
- **Índex automàtics**: Índex general i també de tots els mòduls.
- **Visualització de codi**: Fent servir la llibreria de resaltat [Pygments](http://pygments.org/)
- **Extensible**: [Extensions pròpies](http://www.sphinx-doc.org/en/stable/ext/builtins.html#builtin-sphinx-extensions)
o de tercers [instal·lables des de Pypi](https://pypi.org/search/?q=sphinx+extension)
Té un assistent per la creació de projectes:
```sh
$ pip install Sphinx
$ sphinx-quickstart my-project
```
---
## Documentació d'usuari (on i com?)
- ~~Processadors de text~~
**Al carrer!👋** El descartem directament.
- Formats pesats i normalment són binaris
- Dificultat per fer edició per parts o de forma distribuïda
- Dificultat per gestionar un històric de canvis
- Dificultat per gestionar revisions de canvis i ampliacions
**✨ Spoiler**: [Tracte la documentació com si fos codi](https://github.com/blog/1939-how-github-uses-github-to-document-github)
- Fes servir un format de text pla que pugui ser escrit i editat des de
qualsevol editor.
- Guarda el manual en un repositori de control de versions (Git)
- Podem editar per parts o de forma distribuïda gràcies al control de versions
- Tenim històric!
- Tenim un sistema de *Pull requests* per revisar i aprovar canvis (Merge!)
- Fins i tot podem tenir tests del manual!
---
## Documentació d'usuari (format)
Hi ha dos formats de text pla que són àmpliament reconeguts:
- ReStructuredText
- Markdown (ho està petant!)
---
## Documentació d'usuari (format)
### ReStructuredText vs Markdown
#### ReStructuredText
- 🔝 Sphinx és una de les millors eines de generació de documentació
- 🔝 Molt potent. Suporta referències, notes de peu, bibliografia
- 🔝 Moltes, moltes i moltes extensions
- ❌ Sintàxis força complexe
#### Markdown
- 🔝 Sintaxi senzilla
- ❌ Hi ha diferents "sabors" ➜ [CommonMark](http://commonmark.org/)
- ❌ Poques funcionalitats avançades
---
## Documentació d'usuari (eines)
- Ja hem parlat d'[Sphinx](#14)
- Presentem també una eina per Markdown: [MKdocs](http://www.mkdocs.org/)
- No té tants suports de sortida com ReStructuredText + Sphinx
- D'una forma fàcil pots tenir una pàgina amb manual d'usuari
- Fem més fàcil la col·laboració per membres no tècnics
- Extensible amb totes les extensions de [Python-Markdown](https://pythonhosted.org/Markdown/)
- Publicació: [ReadTheDocs](https://readthedocs.org/)
### Cicle possible
.small[
1. Creem una branca per afegir/modificar documentació
2. Work and commit
3. Push!
4. Pull-request
5. Testing (el format és vàlid, hi ha traduccions?, faltes d'ortografia, ...)
6. Construcció del lloc temporal amb els canvis
7. Revisió
8. Merge
9. Publicació automàtica nova versió]
---
## Testing (Què és?)
**Possible definició**:
>> La prova de programari és un conjunt de processos dirigits a investigar,
avaluar i determinar la **integritat** i la **qualitat** del programari informàtic.
>> Les proves de programari garanteixen el **compliment** d'un producte de programari
en relació **amb els requisits** regulatoris, empresarials, tècnics, funcionals
i d'usuaris.
---
## Testing (Quins hi ha)
Definició per [Robert C. Martin](https://en.wikipedia.org/wiki/Robert_Cecil_Martin)
dels [tipus de tests](http://blog.cleancoder.com/uncle-bob/2017/05/05/TestDefinitions.html).
- **Tests unitaris**: Escrit pel programador per tal que el codi faci el que el
programador espera que faci.
- **Tests d'acceptació**: Escrit per *Negoci* amb el propòsit que el codi faci el
que *Negoci* necessita.
- **Tests d'integració**: Assegurar-se que els **subsistemes** funcionen de forma
correcta entre ells. (No tenen a veure amb regles de *negoci*)
- **Tests de sistema**: Semblants als d'integració però amb **tot el sistema**
- **Micro-tests**: Semblant als tests unitaris, però per una sola funció o
un grup de tests molt reduit.
- **Tests funcionals**: Tests amb un abast més gran amb *mocks* per els components
més lents.
---
## Testing (Quan els fem)
- Abans o deprés?
- Després els tests poden estar *viciats*
- Testejarem **totes** les funcionalitats que hem escrit?
- **TDD al rescat!**
- Desenvolupament guiat per exemples
- [Regles del TDD](http://www.eferro.net/2012/09/resumen-clean-coders-capitulo-6-tdd.html):
- **No** escriure codi de producció si no és perquè un test està fallant
- Escriure el **mínim** d'un test per fer fallar
- Escriure el **mínim** codi necessari per passar el test
- Tenim sempre el test abans d'escriure el codi ➜ **Bona cobertura**
### ♪ Uh! Oh! No tinc por! ♫
- **No ens fa por** netejar el codi
- **No ens fa por** canviar codi
- **No ens fa por** afegir noves funcionalitats
- Avancem **més ràpid**
---
## Testing (eines)
### Execució de tests (UnitTest) + `self.assertX`:
- Llibreria estàndard ([unittest](https://docs.python.org/3/library/unittest.html))
```python
class TestStupidMethod(TestCase):
def test_two_integers_returns_the_sum(self):
result = stupid_method(1, 2)
self.assertEqual(result, 3)
def test_first_param_no_integer_raises_value_error(self):
def raise_value_error():
stupid_method("a", 1)
self.assertRaises(ValueError, raise_valueerror)
```
- Millores a la llibreria estàndard (UnitTesting):
- 🔝 [PyTest](https://docs.pytest.org)
- ❌ [Nose2](http://nose2.readthedocs.io)
---
## Testing (eines)
### Execució de tests (BDD) + [Expects](https://expects.readthedocs.io/en/stable/):
- [Behave](http://pythonhosted.org/behave/)
- [Mamba](http://nestorsalceda.github.io/mamba/)
```python
with description('Using the stupid_method'):
with context('when passing two integers'):
with it('should return the sum of them'):
result = stupid_method(1, 2)
expect(result).to(equal(3))
with context('when passing a string and an integer'):
with it('should raise a ValueError'):
def raises_value_error():
stupid_method("a", 1)
expect(raise_value_error).to(raise_error(ValueError))
```
### Tests funcionals (eines)
- [doublex](http://python-doublex.readthedocs.io/en/latest/)
- [Mock](https://docs.python.org/3/library/unittest.mock.html)
- [vcrpy](http://vcrpy.readthedocs.io/en/latest/)
---
## Integració Contínua (CI)
- Tots els canvis s'han d'integrar a la branca *principal*: **ràpid**
- S'han de passar els tests per comprovar que aquests canvis són correctes
- Evita tenir el *caos* de la release
- Ens permet tenir sempre una versió *testing* (binari)
- Trobem ràpid bugs d'integració
- Obtenir mètriques d'evolució: cobertura, complexitat, fallades.
### Eines
- [Buildbot](https://buildbot.net/) (Python powered)
- [Jenkins](https://jenkins.io/)
- [Drone](https://github.com/drone/drone)
- [Travis](https://travis-ci.org/)
- [CircleCI](https://circleci.com/)
- ...
---
## Desplegament
- Conjunt d'activitats que fa que un sofware sigui utiltizable.
- On? Altres serveis? pre-condicions, post-condicions
- 3 nivells de desplegament
- Manual
- Semi-automàtic
- Automàtic ➜ Augmentar número de desplegaments
### Eines
- [Fabric](http://www.fabfile.org/) + [Fabtools](http://fabtools.readthedocs.io):
Executa ordres remotes a través de SSH
- [Ansible](https://www.ansible.com/application-deployment): Llibres de receptes
amb un sistema de plantilles molt potent i moltes receptes ja fetes
- [dh-virtualenv](http://dh-virtualenv.readthedocs.io/en/latest/): Crea paquets
compatibles amb Debian amb un *virtualenv* autocontingut
---
## Monitoritzar
- Volem fer desplegaments més sovint
- Provar una funcionalitat a producció
- Ser pro-actius
- Més informació de l'error
### Eines
- [Sentry](https://github.com/getsentry/sentry) (Python powered)
---
class: center, middle
# Preguntes?
# 🙈 🙉 🙊
---
class: center, middle
# Moltes gràcies!
</textarea>
<script src="https://remarkjs.com/downloads/remark-latest.min.js">
</script>
<script>
var slideshow = remark.create({
highlightStyle: 'darcula',
highlightLanguage: 'remark',
highlightLines: true,
slideNumberFormat: '%current%/%total%',
ratio: '4:3'
});
</script>
<!-- Global Site Tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-370946-8"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments)};
gtag('js', new Date());
gtag('config', 'UA-370946-8');
</script>
</body>
</html>