Voici un petit point technique sur notre anatomie (hommes et femmes) et une mise en garde sur la pénétration anale à l’aide d’un sex-toy… ou encore d’un objet du quotidien pour les plus économes !
Il faut savoir que cette zone sensible présente une particularité souvent méconnue des amateurs en herbe de sensations fortes ! Tout objet inséré dans l’anus est fort susceptible d’y rester coincer à partir du moment où sa base passe la barrière du sphincter interne (zone musculaire circulaire localisée quelques cm après l’entrée de l’anus) d’où certaines complications majeures faciles à imaginer.
Certains fournisseurs de sex-toys l’indiquent d’ailleurs très clairement dans leurs précautions d’emploi dédiées : pour une stimulation de ce type, il faut impérativement utiliser une référence prévue pour, c’est-à-dire pourvue d’une large base empêchant toute absorption.
Accident domestique ou sexuel, même combat
Si une telle mésaventure devait vous arriver, mieux vaut vous rendre directement aux urgences où le personnel est largement habitué à gérer ce genre d’accidents domestiques…
Oui, et en particulier lorsque le corps étranger incriminé est une bouteille en verre, un bout de manche à balai ou encore un concombre bio, le patient affirme la plupart du temps se l’être malencontreusement introduit en glissant ou en s’asseyant dessus !
Mourir de honte ou mourir, il faut choisir
Car n’imaginez pas que votre joujou va ressortir tout seul. Non non, ce serait bien trop simple. Vous pourriez attendre longtemps avant de vous soulager, tellement longtemps que vous pourriez même en mourir ! Vous l’avez compris, le passage par la case hôpital s’avère indispensable.
Si vous êtes chanceux, vous aurez droit à quelques sédatifs ou une anesthésie générale, le tout accompagné d’une exploration de votre orifice à l’aide d’un spéculum et d’une pince pour retirer l’ustensile. Pour les moins chanceux, direction le bloc opératoire pour une intervention chirurgicale.
Pour en revenir aux sex-toys, les incidents liés semblent en nette hausse d’après les différentes sources : +50% par rapport à 2007 aux Etats-Unis, avec un pic en 2012 associé par certains à la sortie du roman érotique Fifty Shades of Grey !
Sur le vieux continent, le dernier en date recensé évoque un sex-toy de 23 cm coincé dans le rectum d’un irlandais, mais ce dernier semble être un sacré coquin puisqu’il s’agirait d’un récidiviste !
Vous voilà prévenu maintenant.
Et pour terminer en fanfare, une dépêche AFP {risible|tragique}, non loin du sujet !
]]>Du coup cela sera la page de présentation du blog / FAQ / post épinglé du subreddit. Ca tombe bien, ça faisait super longtemps qu’on avait besoin d’un truc comme ça.
On a aussi un article invité qui arrive sur la pénétration anale.
Merci à tous ceux qui participent (correcteurs, modos, users de IE, animateurs sur IRC, etc), on se sent moins seul derrière son clavier.
Ca aide. Ca aide vraiment.
]]>Go mamie, go !
]]>if truc: import pdb; pdb.set_trace() |
Mais il est aussi possible de le faire pendant qu’on est en train d’utiliser le debugger grace à la commande “b” :
b 50, age == 18
Ceci va mettre un breakpoint à la ligne 50 qui s’activera uniquement si age vaut 18.
Des fois que ça serve…
]]>product()
depuis bel lurette, et je n’avais jamais réalisé son utilité. Des fois on a le truc sous les yeux, comme ça, et on voit rien.
Vous savez, on veut parfois parcourir tous les trucs, et leur appliquer tous les machins. Ca donne une boucle imbriquée :
res = [] for truc in trucs: for machin in machins: res.append(bidule(machin, truc)) |
Un code parfaitement légitime, clair, lisible. Mais l’envie de faire une liste en intension est si forte !
C’est là que product()
intervient, avec ses petits bras musclés :
from itertools import product res = [ bidule(machin, truc) for truc, machin in product(trucs, machins)] |
Python va magiquement créer autant de tuples (machin, truc)
qu’il y en a besoin.
Et parce que ça fait un mois que j’ai pas mis d’article, faut prendre tout de suite les bonnes habitudes :
]]>Je publie quand même un article, car :
Tous les projets autour du blog trainent un peu. J’ai what milles tickets ouverts sur github (je les regarde tous les jours pour me donner bonne conscience), mais bon, même S&M ont parfois des mois difficiles ^^ Enfin “ont”… Max lui a trouvé un moyen de doubler son chiffre d’affaires donc il va payer l’apéro.
J’ai été assez actif sur twitter et un peu sur reddit, donc je me dis qu’un truc type “shaarly” ça serait quand même pas mal pour ramener chez nous tout ce contenu qu’on fournit à des services opaques.
Vous avez du noter aussi que le blog tombe souvent en marche. C’est parce que le serveur arrive au bout de sa capacité à héberger nos sites qui prennent trop de mémoire. Va falloir migrer vers une plus grosse instance, aussi plus chère. J’attends un peu.
À tout !
]]>print()
puis du pdb… Puis le code se retrouve en background, ou on a des threads, des sous-processes, des proxies, son serveur WSGI qui tourne, etc. Et là, il faut sortir les outils de logging, la massue, le truc qui demande 3 ans à config.
Il n’y a pas de juste milieu.
Alors un mec nous a donné son Q pour changer tout ça.
pip install q
Q est typiquement un lib de feignasse :
def bip(): a = 1 import q q(a) q(a + 1) bip() |
Et pouf, tout est loggé dans /tmp/q
:
0.0s bip: a=1 0.0s bip: a + 1=2
On peut aussi l’utiliser en décorateur pour tracer l’exécution d’une fonction :
import q @q def bip(): a = 1 bip() |
Ce qui donne :
0.0s bip() 0.0s -> None
Ce n’est bien entendu pas fait pour être laissé dans le code, mais uniquement pour le debug. Néamoins c’est fort pratique :
$TMPDIR
Avec Joe, qui génère des .gitignores, c’est ma petite découverte sympa de la rentrée.
]]>La release est récente, mais fort heureusement on peut facilement l’installer. Sous Windows et Mac, il y a des builds tout chauds.
Pour linux, en attendant un repo tierce partie ou l’upgrade du système, on peut l’installer quelques commandes depuis les sources. Par exemple pour les distros basées sur Debian comme Ubuntu, ça ressemble à :
$ # dependances pour compiler python $ sudo apt-get install build-essential libreadline-dev tk8.4-dev libsqlite3-dev libgdbm-dev libreadline6-dev liblzma-dev libbz2-dev libncurses5-dev libssl-dev python3-dev tk-dev $ sudo apt-get build-dep python3 # juste pour etre sur :) $ # téléchargement des sources $ cd /tmp $ wget https://www.python.org/ftp/python/3.5.0/Python-3.5.0.tar.xz $ tar -xvf Python-3.5.0.tar.xz $ cd Python-3.5.0 $ # et on build $ ./configure $ make $ sudo make altinstall # pas 'make install' qui écrase le python du système ! $ python3.5 # ahhhhhh Python 3.5.0 (default, Sep 16 2015, 10:44:14) [GCC 4.9.2] on linux Type "help", "copyright", "credits" or "license" for more information. >>> |
Sur les centos-likes, c’est grosso merdo la même chose, sans le build-dep (mais plutôt un truc genre sudo yum groupinstall 'Development Tools'
), et en remplaçant les -dev
par -devel
.
@
est maintenant le nouvel opérateur de produit matriciel, mais il ne fait officiellement rien.
Comprenez par là que Python implémente l’opérateur, mais pas le produit en lui-même, la feature ayant été spécialement incluse pour faire plaisir aux utilisateurs de libs scientifiques type numpy.
On va donc tester ça sur le terrain. On se fait un petit env temporaire avec pew et on s’installe numpy :
pew mktmpenv -p python3.5 pip install pip setuptools --upgrade pip install numpy # encore un peu de compilation |
Testons mon bon. L’ancienne manière de faire :
>>> a = np.array([[1, 0], [0, 1]]) >>> b = np.array([[4, 1], [2, 2]]) >>> np.dot(a, b) array([[4, 1], [2, 2]]) |
Et la nouvelle :
>>> a @ b --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-10-3d41f06f59bb> in <module>() ----> 1 a @ b TypeError: unsupported operand type(s) for @: 'numpy.ndarray' and 'numpy.ndarray' |
Woops, apparemment numpy n’a pas encore implémenté le truc.
Bon. Bon, bon, bon. Comment on va tester alors… Ah, oui, y a une magic method :
class Array(np.ndarray): def __matmul__(self, other): return np.dot(self, other) >>> a = a.view(Array) >>> b = b.view(Array) >>> a @ b Array([[4, 1], [2, 2]]) |
Bon, voilà ce que ça donnera quand les devs de numpy auront implémenté le bouzin (la dernière ligne hein, pas tout le bordel avant).
Apparemment ça fait bander les matheux, donc je suppose que c’est une super nouvelle.
En python 2, on pouvait faire "truc %s" % "bidule"
et u"truc %s" % u"bidule"
et b"truc %s" % u"bidule"
et ça a été viré en python 3 qui ne garde %
que pour str
et pas pour bytes
.
Ca n’aurait pas été un problème si ce n’est que Python est très utilisé pour le réseau, et que construire un paquet qui mélange de la sémantique binaire et textuelle devient soudainement une grosse soupe de decode()
et encode()
.
Jour 1, test 3, suspense…
>>> bytearray([66, 108, 117, 101, 32, 112, 114, 105, 101, 115, 116, 32, 115, 97, 121, 115, 58, 32, 37, 115]) % b"wololo" bytearray(b'Blue priest says: wololo') |
Voilà ça c’est fait !
os.path.walk()
est dans mon top 10 des APIs que je déteste le plus en Python, juste à côté de la gestion timezone. Avoir os.walk()
en Python 3 qui retourne un générateur me ravit. Avoir une version 10 X plus rapide avec scandir
, n’est-ce pas choupinet ?
>>> import os >>> list(os.scandir('/tmp/')) [<DirEntry 'systemd-private-316509818ceb41488a4721c78dabb603-colord.service-eXUfPo'>, <DirEntry 'unity_support_test.0'>, <DirEntry 'config-err-7UpWeO'>, <DirEntry '.ICE-unix'>, <DirEntry 'pip-rw_63q0_-unpack'>, <DirEntry 'systemd-private-316509818ceb41488a4721c78dabb603-systemd-timesyncd.service-eZumpq'>] |
C’est très dommage que ça ne retourne pas des objets Path
de pathlib
, mais bon, les perfs, tout ça…
Le saviez-vous ? Python peut exécuter un zip, ce qui permet de créer un script en plusieurs fichiers et de le partager comme un seul fichier. Non vous ne le saviez-vous-te-pas car personne n’en parle jamais.
La 3.5 vient avec un outil en ligne de commande pour faciliter la création de tels zip et une nouvelle extension (que l’installeur fera reconnaitre à Windows) pour cesdits fichiers : .pyz.
Je fais mon script :
foo ├── bar.py ├── __init__.py └── __main__.py |
__main__.py est obligatoire, c’est ce qui sera lancé quand on exécutera notre script. Dedans je mets import bar
et dans bar print('wololo again')
.
Ensuite je fusionne tout ça :
python -m zipapp foo |
Et pouf, j’ai mon fichier foo.pyz :
$ python3.5 foo.pyz wololo again |
Attention aux imports dedans, ils sont assez chiants à gérer.
J’adore cette feature. J’adore toutes les features de la 3.5. Cette release est fantastique. Depuis la 3.3 chaque release est fantastique.
Mais bon, zeste de savoir l’a traité en long et en large donc rien à dire de plus, si ce n’est que j’avais raté un GROS truc :
Donc ces syntaxes sont valides :
>>> *range(2), *[1, 3], *'ea' (0, 1, 1, 3, 'e', 'a') >>> *[x * x for x in range(3)], *{"a": 1}.values() (0, 1, 4, 1) |
Ce qui peut être très chouette et aussi la porte ouverte à l’implémentation d’un sous-ensemble de Perl en Python. C’est selon l’abruti qui code.
Ce qu’il faut bien comprendre avec les types hints, c’est que Python ne s’en sert pas. Il n’en fait rien. Que dalle. Nada. Peau de balle. Zob. Niet. Zero. La bulle. Néant. Null. None. Réforme gouvernementale.
Les types hints sont disponibles, mais Python ne va pas les traiter différemment d’autres annotations. Le but est de permettre à des outils externes (linter, IDE, etc) de se baser sur ces informations pour ajouter des fonctionnalités.
Pour l’instant, un seul le fait : mypy.
Et là on sent bien que tout ça est tout neuf car si on fait pip install mypy-lang
, on tombe sur une version buggée. Il faut donc l’installer directement depuis le repo, soit :
pip install https://github.com/JukkaL/mypy/archive/master.zip |
Puis écriture d’une fonction annotée avec des types hints :
from typing import Iterable, Tuple PixelArray = Iterable[Tuple[int, int, int]] def rgb2hex(pixels: PixelArray) -> list: pattern = "#{0:02x}{1:02x}{2:02x}" return [pattern.format(r, g, b) for r, g, b in pixels] # ça marche : rgb2hex([(1, 2, 3), (1, 2, 3)]) # ['#010203', '#010203'] |
La preuve que Python n’en fait rien :
>>> hex("fjdkls") --------------------------------------------------------------------------- TypeError Traceback (most recent call last) <ipython-input-2-be62b8f062fe> in <module>() ----> 1 hex("fjdkls") TypeError: 'str' object cannot be interpreted as an integer |
Même la doc profite peu du typage :
Help on function rgb2hex in module __main__: rgb2hex(pixels:typing.Iterable) -> list
Mais si on met ça dans un fichier foo.py :
from essai import rgb2hex print(rgb2hex("fdjksl")) res = rgb2hex([(1, 2, 3), (3, 4, 5)]) print(res + 1) |
Et qu’on le passe à la moulinette :
$ mypy foo.py foo.py:3: error: Argument 1 to "rgb2hex" has incompatible type "str"; expected Iterable[...] foo.py:5: error: Unsupported operand types for + (List[Any] and "int") |
Ensuite j’ai essayé de créer un stub file, c’est-à-dire de mettre les hints dans un fichier à part plutôt que directement dans le code. Ma fonction redevient :
def rgb2hex(pixels): pattern = "#{0:02x}{1:02x}{2:02x}" return [pattern.format(r, g, b) for r, g, b in pixels] |
Et mon fichier stub (même nom, mais avec extension .pyi) contient :
from typing import Iterable, Tuple PixelArray = Iterable[Tuple[int, int, int]] def rgb2hex(pixels: PixelArray) -> list:... |
Les stubs sont donc bien des fichiers Python valides, mais avec une extension différente, et juste les signatures des fonctions (le corps du bloc est une Ellipsis
).
Et poof, ça marche pareil :
$ mypy foo.py foo.py:3: error: Argument 1 to "rgb2hex" has incompatible type "str"; expected Iterable[...] foo.py:5: error: Unsupported operand types for + (List[Any] and "int") |
Il y a un repo qui contient des fichiers stubs pour la stdlib. Vous pouvez y participer, c’est un moyen simple de contribuer à Python.
Bref, pour le moment ça demande encore un peu de maturité, mais je pense que d’ici quelques mois on aura des outils bien rodés pour faire tout ça automatiquement.
La feature pub. Techniquement le truc qui a fait dire à tous ceux qui voulaient de l’asyncrone que Python en fait, c’était trop cool. Sauf que Python pouvait faire ça avec yield from
avant, mais c’est sur que c’était super confusionant.
Maintenant on a un truc propre : pas de décorateur @coroutine
, pas de syntaxe semblable aux générateurs, mais des méthodes magiques comme __await__
et de jolis mots-clés async
et await
.
Vu que Crossbar est maintenant compatible Python 3, et qu’il supporte asyncio pour les clients… Si on s’implémentait un petit wrapper WAMP pour s’amuser à voir ce que ressemblerait une API moderne pour du Websocket en Python ?
pip install crossbar
crossbar init
crossbar start |
(Ouhhhh, plein de zolies couleurs apparaissent dans ma console ! Ils ont fait des efforts cosmétiques chez Tavendo)
Bien, voici maintenant l’exemple d’un client WAMP de base codé avec asyncio selon l’ancienne API :
import asyncio from autobahn.asyncio.wamp import ApplicationSession, ApplicationRunner class MyComponent(ApplicationSession): @asyncio.coroutine def onJoin(self, details): # on marque cette fonction comme appelable # a distance en RPC def add(a, b): return a + b self.register(add, "add") # et on triche en l'appelant cash. J'ai # la flemme de coder un deuxième client # et ça passe quand même par le routeur # donc merde res = yield from self.call("add", 2, 3) print("Got result: {}".format(res)) if __name__ == '__main__': runner = ApplicationRunner("ws://127.0.0.1:8080/ws", u"crossbardemo", debug_wamp=False, # optional; log many WAMP details debug=False, # optional; log even more details ) runner.run(MyComponent) |
Et ça marche nickel en 3.5. Mais qu’est-ce que c’est moche !
On est en train de bosser sur l’amélioration de l’API, mais je pense que ça va reste plus bas niveau que je le voudrais.
Donc, amusons-nous un peu à coder un truc plus sexy. Je vous préviens, le code du wrapper est velu, j’avais envie de me marrer un peu après les exemples ballots plus haut :
import asyncio from autobahn.asyncio.wamp import ApplicationSession, ApplicationRunner class App: def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.procedures = [] self.subscriptions = [] self.event_handlers = {} def run(self, url="ws://127.0.0.1:8080/ws", realm="realm1", debug_wamp=False, debug=False): runner = ApplicationRunner(url, realm, debug_wamp=debug_wamp, debug=debug) runner.run(self) def run_cmd(self, *args, **kwargs): # et on pourrait même ici mettre du parsing d'argument # et de os.environ, mais j'ai la flemme if __name__ == '__main__': self.run(*args, **kwargs) # quelques décorateurs pour faire du déclaratif # et remettre les paramètres dans le bon ordre def register(self, name, *args, **kwargs): def wrapper(proc): self.procedures.append([name, proc, args, kwargs]) return proc return wrapper def subscribe(self, topic, *args, **kwargs): def wrapper(callback): self.procedures.append([topic, callback, args, kwargs]) return callback return wrapper # un système d'event interne def on(self, event): def wrapper(callback): self.event_handlers.setdefault(event, []).append(callback) return callback return wrapper async def trigger(self, event): for callback in self.event_handlers.get(event, ()): await callback(self.session) # un peu de code de compatibilité avec l'API initiale def __call__(self, *args): class CustomeSession(ApplicationSession): async def onJoin(session_self, details): # on joint on fait tous les registers et tous les # subscribes for name, proc, args, kwargs in self.procedures: session_self.register(proc, name, *args, **kwargs) for topic, callback, args, kwargs in self.subscriptions: session_self.subscribe(proc, topic, *args, **kwargs) # on appelle les handlers de notre event await self.trigger('joined') self.session = CustomeSession(*args) return self.session |
Évidement la coloration syntaxique ne suit pas sur nos async
/await
.
Bon, vous allez me dire, mais ça quoi ça sert tout ça ? Et bien, c’est une version tronquée et codée à l’arrache de l’API Application pour Twisted… mais version asyncio.
C’est-à-dire que c’est une lib qui permet de faire le même exemple que le tout premier qu’on a vu dans cette partie – qui souvenez-vous était fort moche -, mais comme ça :
app = App() @app.register('add') async def add(a, b): return a + b @app.on('joined') async def _(session): res = await session.call("add", 2, 3) print("Got result: {}".format(res)) app.run_cmd() |
Des jolis décorateurs ! Des jolis async ! Des jolis await !
Et tout ça tourne parfaitement sur 3.5 messieurs-dames.
Bref, on peut faire du WAMP avec une syntaxe claire et belle, il faut juste se bouger le cul pour coder une abstraction un peu propre.
Je pense que autobahn restera toujours un peu bas niveau. Donc il va falloir que quelqu’un se colle à faire une lib pour wrapper tout ça.
Des volontaires ?
Arf, je savais bien que ça allait me retomber sur la gueule.
]]>Cette nouvelle release contient beaucoup de choses qui corrigent ou pallient un paquet de trucs relou dans le routeur Crossbar (et par conséquent la lib client Autobahn) :
Pour la suite, ils travaillent sur la doc, et l’amélioration de l’API. En attendant, on peut pip install crossbar
et profiter de ces nouveautés sans avoir à passer par github.
De mon côté j’ai un article sur l’authentification avec Crossbar dans les cartons. ETA dans les 10 prochains jours.
]]>Je suis tombé dessus par hasard, je ne sais plus trop comment, et j’ai réalisé que j’avais changé d’avis sur l’article. Je ne recommandais plus du tout ce que j’y mettais.
Il fallait le mettre à jour, mais un si petit article, je serais probablement passé à côté dans le futur. En effet, je ne veux pas updater 600 articles, donc je fais ceux qui me paraissent prioritaires.
Du coup je l’ai fait tout de suite, histoire de ne pas oublier. En prime, il m’a donné envie d’écrire un article sur le formatage des chaines en Python en général, qui devrait sortir dans la semaine.
Cette réécriture comprend :
On se chauffe les yeux, et on va le lire !
Dans le cadre de notre parenthèse lubrique, j’ai eu envie de ruiner l’enfance de la nouvelle génération, la règle 34 ayant suffisamment attaqué la mienne :
Je suis quand même épaté de tout cet effort déployé par les monstres de tout poil (mouarf) juste pour insérer leur tentacules dans des orifices. Mais bon, quand ce sont des bites, au Japon, elles doivent être floutées, du coup ceci explique cela. Et puis ce qu’on trouve sur MyLittlePoney n’est pas mieux.
]]>