Compare commits
8
Commits
_170926_A
...
e129991fcd
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e129991fcd | ||
|
|
66449003d3 | ||
|
|
e558a08fff | ||
|
|
87204f8627 | ||
|
|
e352bcf4cb | ||
|
|
df0de2c695 | ||
|
|
f632a89c2e | ||
|
|
54c0874b5a |
@@ -25,7 +25,7 @@ ENVIAR3_ARQUIVO_ORDENS=ordens3.json
|
||||
ENVIAR3_QUANTIDADE_ORDENS=2
|
||||
|
||||
# ═══ Envio Automático 4 ═════════════════════════════════════════════
|
||||
ENVIAR4_ATIVO=s
|
||||
ENVIAR4_ATIVO=n
|
||||
ENVIAR4_INTERVALO_MINUTOS=10
|
||||
ENVIAR4_ARQUIVO_ORDENS=ordens4.json
|
||||
ENVIAR4_QUANTIDADE_ORDENS=2
|
||||
@@ -42,6 +42,18 @@ ENVIAR6_INTERVALO_MINUTOS=1
|
||||
ENVIAR6_ARQUIVO_ORDENS=ordens6.json
|
||||
ENVIAR6_QUANTIDADE_ORDENS=2
|
||||
|
||||
# ═══ Envio Automático 7 ═════════════════════════════════════════════
|
||||
ENVIAR7_ATIVO=n
|
||||
ENVIAR7_INTERVALO_MINUTOS=1
|
||||
ENVIAR7_ARQUIVO_ORDENS=ordens7.json
|
||||
ENVIAR7_QUANTIDADE_ORDENS=2
|
||||
|
||||
# ═══ Envio Automático 8 ═════════════════════════════════════════════
|
||||
ENVIAR8_ATIVO=s
|
||||
ENVIAR8_INTERVALO_MINUTOS=1
|
||||
ENVIAR8_ARQUIVO_ORDENS=ordens8.json
|
||||
ENVIAR8_QUANTIDADE_ORDENS=2
|
||||
|
||||
# ═══ Horário de funcionamento (formato HH:MM, 24h) ═══════════════════
|
||||
HORA_INICIO=08:00
|
||||
HORA_FIM=22:00
|
||||
|
||||
@@ -48,6 +48,18 @@ ENVIAR6_INTERVALO_MINUTOS=1
|
||||
ENVIAR6_ARQUIVO_ORDENS=ordens6.json
|
||||
ENVIAR6_QUANTIDADE_ORDENS=1
|
||||
|
||||
# ═══ Envio Automático 7 ═════════════════════════════════════════════
|
||||
ENVIAR7_ATIVO=s
|
||||
ENVIAR7_INTERVALO_MINUTOS=1
|
||||
ENVIAR7_ARQUIVO_ORDENS=ordens7.json
|
||||
ENVIAR7_QUANTIDADE_ORDENS=1
|
||||
|
||||
# ═══ Envio Automático 8 ═════════════════════════════════════════════
|
||||
ENVIAR8_ATIVO=s
|
||||
ENVIAR8_INTERVALO_MINUTOS=1
|
||||
ENVIAR8_ARQUIVO_ORDENS=ordens8.json
|
||||
ENVIAR8_QUANTIDADE_ORDENS=1
|
||||
|
||||
# ═══ Horário de funcionamento (formato HH:MM, 24h) ═══════════════════
|
||||
HORA_INICIO=08:00
|
||||
HORA_FIM=22:00
|
||||
@@ -32,7 +32,7 @@ Preencha:
|
||||
|
||||
### Envios automáticos
|
||||
|
||||
O sistema suporta até 6 envios automáticos independentes (1 a 6):
|
||||
O sistema suporta até 8 envios automáticos independentes (1 a 8):
|
||||
|
||||
| Variável | Descrição |
|
||||
|---|---|
|
||||
@@ -49,7 +49,7 @@ ENVIAR1_INTERVALO_MINUTOS=1
|
||||
ENVIAR1_ARQUIVO_ORDENS=ordens1.json
|
||||
```
|
||||
|
||||
Cada envio usa seu próprio arquivo de ordens (`ordens1.json` a `ordens6.json`). Não modifique a ordem dos itens dentro de cada JSON.
|
||||
Cada envio usa seu próprio arquivo de ordens (`ordens1.json` a `ordens8.json`). Não modifique a ordem dos itens dentro de cada JSON.
|
||||
|
||||
## 4. Testar manualmente
|
||||
|
||||
@@ -109,7 +109,7 @@ sudo systemctl disable jogo3
|
||||
|
||||
## 7. Atualizar ordens
|
||||
|
||||
Edite o arquivo de ordens desejado (`ordens1.json` … `ordens6.json`), depois reinicie o service:
|
||||
Edite o arquivo de ordens desejado (`ordens1.json` … `ordens8.json`), depois reinicie o service:
|
||||
|
||||
```bash
|
||||
sudo systemctl restart jogo3
|
||||
|
||||
@@ -4,8 +4,8 @@ Bot do Telegram que envia ordens eróticas automáticas em intervalos configurá
|
||||
|
||||
## Funcionalidades
|
||||
|
||||
- **Múltiplos envios automáticos** — até 6 canais independentes rodando em paralelo (`ENVIAR1` a `ENVIAR6`), cada um com seu próprio arquivo de ordens, intervalo e quantidade por ciclo.
|
||||
- **Arquivos de ordens em JSON** — ordens são lidas de arquivos JSON (`ordens1.json` … `ordens6.json`) com total liberdade de edição. Cada arquivo tem seu próprio contexto temático (ex: `ordens4.json` foco em CABEÇA do pau, `ordens5.json` sem dedo no cú, `ordens6.json` permite dedo no cú).
|
||||
- **Múltiplos envios automáticos** — até 8 canais independentes rodando em paralelo (`ENVIAR1` a `ENVIAR8`), cada um com seu próprio arquivo de ordens, intervalo e quantidade por ciclo.
|
||||
- **Arquivos de ordens em JSON** — ordens são lidas de arquivos JSON (`ordens1.json` … `ordens8.json`) com total liberdade de edição. Cada arquivo tem seu próprio contexto temático (ex: `ordens4.json` foco em CABEÇA do pau, `ordens7.json` foco em exposição ao ar livre).
|
||||
- **Adaptação automática de gênero** — se `SEXO=feminino` no `.env`, o sistema substitui termos masculinos (pau, saco, cueca, punheta, porra, próstata) pelos equivalentes femininos (buceta, xota, calcinha, siririca, gozo, ponto G). Arquivos `ordens2.json` e `ordens3.json` usam "Ação sensual" como placeholder para substituição contextual.
|
||||
- **Controle de horário** — as ordens só são enviadas dentro da janela configurada (`HORA_INICIO` / `HORA_FIM`).
|
||||
- **Ordens por ciclo** — cada canal pode enviar 1 ou mais ordens a cada intervalo, permitindo rajadas ou envios individuais.
|
||||
@@ -44,7 +44,7 @@ chmod +x iniciar.sh
|
||||
|
||||
### Canais de envio (`ENVIAR{n}_*`)
|
||||
|
||||
Cada canal **1 a 6** tem sua própria configuração:
|
||||
Cada canal **1 a 8** tem sua própria configuração:
|
||||
|
||||
| Variável | Descrição |
|
||||
|---|---|
|
||||
@@ -63,7 +63,7 @@ ENVIAR1_QUANTIDADE_ORDENS=2
|
||||
|
||||
### Arquivos de ordens
|
||||
|
||||
Os arquivos `ordens1.json` a `ordens6.json` contêm listas JSON com strings de ordens. Cada ordem é uma string descritiva com instruções de execução detalhadas. Contexto de cada arquivo:
|
||||
Os arquivos `ordens1.json` a `ordens8.json` contêm listas JSON com strings de ordens. Cada ordem é uma string descritiva com instruções de execução detalhadas. Contexto de cada arquivo:
|
||||
|
||||
| Arquivo | Ordens | Contexto |
|
||||
|---|---|---|
|
||||
@@ -73,6 +73,8 @@ Os arquivos `ordens1.json` a `ordens6.json` contêm listas JSON com strings de o
|
||||
| `ordens4.json` | 100 | Foco em CABEÇA do pau — massagens, círculos, estímulos da glande |
|
||||
| `ordens5.json` | 100 | CABEÇA do pau + pau + exibir bunda/cú. **Sem dedo no cú. Sem gozar.** |
|
||||
| `ordens6.json` | 100 | CABEÇA do pau + pau + exibir bunda/cú. **Permite dedo no cú. Sem gozar.** |
|
||||
| `ordens7.json` | 400 | **Exposicao + sexo ao ar livre** |
|
||||
| `ordens8.json` | 100 | **Dominacao/humilhacao** — tapas, edging, ruined orgasm, multiplos orgasmos, humilhacao verbal, castigo corporal, dominacao imaginaria, submissao total |
|
||||
|
||||
**Regras ao editar/criar ordens:**
|
||||
- O arquivo deve conter um array JSON válido de strings
|
||||
@@ -95,6 +97,8 @@ app/
|
||||
├── ordens4.json
|
||||
├── ordens5.json
|
||||
├── ordens6.json
|
||||
├── ordens7.json
|
||||
├── ordens8.json
|
||||
├── dados_*.py # Bancos de dados auxiliares (não usados pelo main.py atualmente)
|
||||
├── DEPLOY.md # Instruções de deploy no Debian 12
|
||||
├── README.md # Este arquivo
|
||||
|
||||
Binary file not shown.
+4
-4
@@ -30,7 +30,7 @@ HORA_FIM=$(grep_val HORA_FIM)
|
||||
|
||||
# Configuração de cada envio automático
|
||||
declare -a ENVIAR_ATIVO ENVIAR_INTERVALO ENVIAR_ARQUIVO ENVIAR_QUANTIDADE
|
||||
for n in 1 2 3 4 5 6; do
|
||||
for n in 1 2 3 4 5 6 7 8; do
|
||||
ENVIAR_ATIVO[$n]=$(grep_val "ENVIAR${n}_ATIVO")
|
||||
ENVIAR_INTERVALO[$n]=$(grep_val "ENVIAR${n}_INTERVALO_MINUTOS")
|
||||
ENVIAR_ARQUIVO[$n]=$(grep_val "ENVIAR${n}_ARQUIVO_ORDENS")
|
||||
@@ -47,7 +47,7 @@ if [ -z "$TELEGRAM_CHAT_ID" ] || [ "$TELEGRAM_CHAT_ID" = "seu_chat_id_aqui" ]; t
|
||||
fi
|
||||
|
||||
# Validação dos arquivos de ordens
|
||||
for n in 1 2 3 4 5 6; do
|
||||
for n in 1 2 3 4 5 6 7 8; do
|
||||
if [ "${ENVIAR_ATIVO[$n]}" = "s" ] && [ ! -f "${ENVIAR_ARQUIVO[$n]}" ]; then
|
||||
echo "[!] Arquivo de ordens '${ENVIAR_ARQUIVO[$n]}' não encontrado (envio automático $n)"
|
||||
exit 1
|
||||
@@ -55,7 +55,7 @@ for n in 1 2 3 4 5 6; do
|
||||
done
|
||||
|
||||
export TELEGRAM_TOKEN TELEGRAM_CHAT_ID SEXO IDADE HORA_INICIO HORA_FIM
|
||||
for n in 1 2 3 4 5 6; do
|
||||
for n in 1 2 3 4 5 6 7 8; do
|
||||
export "ENVIAR${n}_ATIVO=${ENVIAR_ATIVO[$n]}"
|
||||
export "ENVIAR${n}_INTERVALO_MINUTOS=${ENVIAR_INTERVALO[$n]}"
|
||||
export "ENVIAR${n}_ARQUIVO_ORDENS=${ENVIAR_ARQUIVO[$n]}"
|
||||
@@ -99,7 +99,7 @@ echo "[+] Iniciando sistema de ordens..."
|
||||
echo " Sexo: ${SEXO:-masculino} | Idade: ${IDADE:-30}"
|
||||
echo " Horário: ${HORA_INICIO:-08:00} - ${HORA_FIM:-22:00}"
|
||||
echo ""
|
||||
for n in 1 2 3 4 5 6; do
|
||||
for n in 1 2 3 4 5 6 7 8; do
|
||||
status="❌ DESATIVADO"
|
||||
if [ "${ENVIAR_ATIVO[$n]}" = "s" ]; then
|
||||
status="✅ ATIVO"
|
||||
|
||||
@@ -202,6 +202,8 @@ def main():
|
||||
carregar_config_envio(4),
|
||||
carregar_config_envio(5),
|
||||
carregar_config_envio(6),
|
||||
carregar_config_envio(7),
|
||||
carregar_config_envio(8),
|
||||
]
|
||||
asyncio.run(_executar_todos_envios(configs))
|
||||
except KeyboardInterrupt:
|
||||
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
[
|
||||
"🔥 EXPOSTO JANELA PEgo — Vai ate o quarto, abre a janela BEM. Tira TUDO. Fica de FRENTE pra janela, corpo nu, pau duro exposto. 2 minutos se exibindo. De repente UMA VIZINHA aparece na janela ao lado e te OLHA fixo. Ela abre a boca de surpresa mas nao desvia o olhar. Ela PERGUNTA: 'Ta mostrando o que pra mim, gostoso?' Tu RESPONDE: 'O que a senhora quiser ver.' Ela MANDA: 'VIRA DE COSTAS E MOSTRA ESSA BUNDA.' Tu VIRA e mostra a bunda escancarada pra ela. 1 minuto de bunda pro olhar da vizinha. Depois ela ri e FECHA a janela. Tu massageia o pau. Nao goza, sua puta exibida pra vizinha.",
|
||||
"🔥 EXPOSTO AREA SERVICO PEGO — Vai ate a AREA DE SERVICO, ABRE a porta pro quintal. Tira calça e cueca, fica de camiseta. Debruca na MAQUINA DE LAVAR, bunda empinada pro lado de fora. De repente o VIZINHO aparece no muro e TE VE de bunda pro ar. Ele ASSOBIA e grita: 'E ai, ta se oferecendo?' Tu CONGELA mas nao sai da posicao. Ele MANDA: 'BALANCA ESSA BUNDA PRA MIM.' Tu BALANCA a bunda devagar 10 vezes enquanto ele olha. Depois ele da UM TAPA na tua bunda por cima do muro e vai embora. Tu fecha a porta e massageia a bunda. Nao goza, seu puto pego no flagra.",
|
||||
"🔥 EXPOSTO QUINTAL PEGO — Vai ate o QUINTAL, tira TUDO atras do muro. Fica de COSTAS pro mundo, mãos na nuca, pau e bunda expostos. Uma MULHER passa na rua e TE VE por cima do muro. Ela PARA, OLHA, e da risada. 'Nossa, que ousadia!' Tu fica imovel, o coração acelerado. Ela MANDA: 'SE VIRA DE FRENTE PRA MIM.' Tu VIRA e mostra o pau duro. Ela OLHA fixo e diz: 'COÇA ESSA CABECINHA PRA MIM.' Tu alisa a cabecinha com o dedo enquanto ela assiste. 1 minuto de show particular. Depois ela VAI EMBORA. Tu veste e volta. Nao goza, sua puta.",
|
||||
"🔥 EXPOSTO BIKE PEGO 1 — Vai de BIKE PELADO — só de camiseta — pra lugar DESERTO. PEDALA 3 minutos. Uma OUTRA BIKE aparece no horizonte vindo na direcao. E um HOMEM ou MULHER ciclista. Tu NAO TEM TEMPO de vestir. Ele/ela CHEGA PERTO e TE VE pelado na bike. PARA a bike e FALA: 'Caralho, ta pelado memo hein.' Tu fica sem graça mas sentado na bike, pau exposto. Ele MANDA: 'DESCE DESSA BIKE E MOSTRA ESSE CU.' Tu DESCE e fica de COSTAS, bunda exposta. Ele OLHA e diz: 'PODE SUBIR DE VOLTA, PUTA.' Tu monta e pedala embora. Nao goza, seu puto pego de bike.",
|
||||
"🔥 EXPOSTO BIKE PEGO 2 — Vai de BIKE PELADO pra estrada de terra deserta. PEDALA 4 minutos. Um CAMINHONEIRO para o caminhao no acostamento e TE VE pelado pedalando. Ele DESCE do caminhao, cruza os braços e OLHA. 'Cê ta doido, mermao?' Tu PARA a bike. Ele MANDA: 'ENCOSTA A BIKE ALI E VEM CA.' Tu encosta a bike e vai ate perto. Ele OLHA teu corpo nu e diz: 'VIROU MINHA ATRAÇÃO DE ESTRADA, HEIN? AGORA VAI ANDANDO PELADO ALI NA FRENTE ENQUANTO EU OLHO.' Tu caminha pelado 1 minuto na frente dele enquanto ele assiste. Depois ele da risada, entra no caminhao e vai embora. Tu monta na bike e volta. Nao goza, seu puto de estrada.",
|
||||
"🔥 EXPOSTO BIKE PEGO 3 — Vai de BIKE PELADO pra lugar DESERTO. DESCE da bike e COMECA a se MASTURBAR em pé. Uma PESSOA aparece caminhando com um CACHORRO. O cachorro late, a pessoa TE VE. 'Nossa, que absurdo!' Mas a pessoa NAO SAI — fica olhando. Tu com a mão no pau, imovel. ELA MANDA: 'ABRE ESSAS PERNAS PRA EU VER.' Tu abre as pernas, pau exposto. Ela OLHA e diz: 'TA BOM, PODE CONTINUAR SEU TREINO DE PUTA.' Depois vai embora puxando o cachorro. Tu monta na bike e volta rapido. Nao goza, sua puta pega no flagra.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO 1 — Vai pra uma TRILHA ou CAMINHO DESERTO. Tira TUDO. CAMINHA PELADO 2 minutos. Um HOMEM aparece na trilha vindo de frente. Tu CONGELA. Ele CHEGA PERTO e OLHA teu corpo nu dos pés a cabeça. 'Cê ta maluco, ta nu no meio do mato?' Tu fala: 'To me exercitando.' Ele da risada e MANDA: 'EXERCITA ESSA BUNDA ENTÃO — ENCOSTA NAQUELA ARVORE E MOSTRA O CU.' Tu vai ate a arvore, se curva e mostra o cu escancarado pra ele. Ele OLHA 30 segundos e depois VAI EMBORA. Tu veste e volta. Nao goza, seu puto exposto na trilha.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO 2 — Vai pra um CAMPO ABERTO e VAZIO. Tira TUDO. CAMINHA PELADO. Uma MULHER de bicicleta passa PERTO e TE VE. Ela PARA, DESCE da bike e VEM na sua direcao. 'Oi, vi voce pelado e vim ver de perto.' Tu fica sem reação. Ela OLHA teu pau e pergunta: 'POSSO TOCAR?' Tu balança a cabeca que sim. Ela ENCOSTA a mão no teu pau e alisa a cabecinha por 30 segundos. Depois ELA MANDA: 'AGORA ME MOSTRA ESSA BUNDA.' Tu vira e mostra a bunda. Ela da UM TAPA e monta na bike. 'ATÉ MAIS, PUTA.' Ela vai embora. Tu veste e volta. Nao goza, sua puta tocada pela estranha.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO 3 — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Duas MULHERES aparecem caminhando e TE VEEM. Elas PARAM e uma delas PERGUNTA: 'Ta fazendo um filme?' Tu nega. Elas se OLHAM e riem. Uma delas MANDA: 'DA UMA VOLTINHA PRA GENTE VER.' Tu gira 360 graus mostrando o corpo nu. Elas APLAUDEM. A outra diz: 'AGORA FAZ 5 FLEXÕES PELADO.' Tu faz 5 flexões no chao, o pau tocando a grama. Elas riem e vao embora. 'Gostamos do show!' Tu levanta e veste. Nao goza, sua puta apresentadora.",
|
||||
"🔥 EXPOSTO CAMINHADA EDGING PEGO — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO 2 minutos. Senta NU numa pedra e COMECA a se MASTURBAR. Um HOMEM aparece vindo de longe. Tu VE ele mas CONTINUA se tocando. Ele CHEGA PERTO e OLHA a cena. 'Ta se divertindo?' Tu nao para. Ele SENTA do lado e ASSISTE. Tu masturba ate QUASE GOZAR e PARA no limite, na frente dele. Ele APLAUDE: 'Que autocontrole.' Depois LEVANTA e vai embora. Tu fica com o pau pulsando e volta pra casa. Nao goza, sua puta exibida pro estranho.",
|
||||
"🔥 EXPOSTO QUARTO VIZINHA — Vai ate a janela do quarto, ABRE. Tira TUDO. Fica de FRENTE pra janela se exibindo. A VIZINHA esta na sacada dela e TE VE. Ela cruza os braços e OLHA. 'De novo mostrando o presente?' Tu sorri sem graça. Ela MANDA: 'BATE UMA PALMINHA PRA EU VER SE ESSA BUNDA MARCA BEM.' Tu se curva e da 10 tapas na propria bunda enquanto ela olha da sacada. Depois ela manda um beijo e entra. Nao goza, sua puta da vizinha.",
|
||||
"🔥 EXPOSTO AREA SERVICO VIZINHO — Vai ate a AREA DE SERVICO, deixa a porta ENTREABERTA. Tira calça e cueca. Fica de QUATRO, cu exposto na fresta. O VIZINHO aparece do outro lado do muro e VE o cu na fresta. Ele EMPURRA a porta devagar e te VE de quatro de cu aberto. 'Que isso, ta me esperando?' Tu nao responde. Ele MANDA: 'ABRE MAIS ESSE CU COM A MÃO.' Tu abre o cu com os dedos pra ele ver. Ele OLHA por 1 minuto e depois FECHA a porta devagar e vai embora. Tu levanta e massageia o cu. Nao goza, seu puto oferecido.",
|
||||
"🔥 EXPOSTO QUINTAL VIZINHA — Vai ate o QUINTAL, tira TUDO. Fica de quatro perto do muro, cu exposto. A VIZINHA do lado esta no quintal dela e TE VE pelo buraco do muro ou por cima. 'Ta mostrando o rego de novo, hein?' Tu fica imovel. Ela MANDA: 'ENFIA O DEDO NO CU E MOSTRA PRA MIM.' Tu enfia o dedo no cu e abre enquanto ela olha pelo muro. 1 minuto de dedo no cu pra vizinha. Depois ela volta pro que tava fazendo. Tu sai de quatro. Nao goza, sua puta vizinhada.",
|
||||
"🔥 EXPOSTO BIKE PEGO 4 — Vai de BIKE PELADO. Um CARRO passa devagar e o MOTORISTA TE VE. Ele REDUZ a velocidade, ABRE a janela e GRITA: 'E aí, ciclista sem vergonha!' Tu pedala sem reagir. Ele MANDA: 'PARA A BIKE.' Tu PARA. Ele DESCE do carro e se APROXIMA. 'Mostra esse negocio direito.' Ele OLHA teu pau de perto. 'BATE UMA PUNHETA PRA EU VER SE É BOM.' Tu comeca a masturbar enquanto ele assiste. 'BATE ATÉ QUASE GOZAR E PARA.' Tu quase goza e PARA. Ele da risada, entra no carro e vai embora. Tu monta e volta. Nao goza, sua puta de estrada.",
|
||||
"🔥 EXPOSTO BIKE PEGO 5 — Vai de BIKE PELADO. Duas AMIGAS estao caminhando e TE VEEM passar pelado. Elas GRITAM: 'EI, PELADO!' e CORREM atras. Tu PARA a bike. Elas CHEGAM PERTO, ofegantes, e OLHAM teu corpo nu. Uma delas PASSA a mão no teu peito e diz: 'Ta duro.' A outra MANDA: 'DESCE DA BIKE.' Tu desce. Ela MANDA: 'ENCOSTA A BIKE E VEM CA — MOSTRA PRA GENTE COMO CÊ ANDA PELADO.' Tu caminha 1 minuto pelado na frente delas enquanto elas olham e comentam entre si. Depois se DESPEDEM e vao embora. Tu monta e volta. Nao goza, sua puta desfilando pras amigas.",
|
||||
"🔥 EXPOSTO CAMINHADA CASAL — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO 2 minutos. Um CASAL aparece caminhando de maos dadas. Eles TE VEEM e PARAM. O HOMEM da risada, a MULHER OLHA curiosa. O HOMEM PERGUNTA: 'Ta vendendo alguma coisa?' ELE MANDA: 'DA UMA VOLTA PROS NOS VER.' Tu gira mostrando o corpo. A MULHER pergunta: 'POSSO TOCAR?' Tu deixa. Ela ENCOSTA a mão na tua bunda e APERTA. O homem MANDA: 'AGORA ABRE AS PERNAS.' Tu abre as pernas, pau exposto. Eles OLHAM e vao embora de maos dadas. Tu veste e volta. Nao goza, sua puta inspecionada.",
|
||||
"🔥 EXPOSTO CAMINHADA GRUPO — Vai pra lugar SEMI-DERTO. Tira TUDO. COMECA a caminhar pelado. Um GRUPO de 3 PESSOAS aparece (duas mulheres e um homem). Eles TE VEEM e PARAM. 'Olha o maluco pelado!' Eles se APROXIMAM e um deles PERGUNTA: 'Ta fazendo o que?' Tu diz: 'So caminhando.' O HOMEM MANDA: 'ENTAO CAMINHA ALI PRA GENTE VER.' Tu caminha pelado na frente deles. As mulheres RIEM e UMA DELAS MANDA: 'DA UMA REBOLADA PRA GENTE.' Tu rebola 10 vezes enquanto eles assistem. Depois eles VAO EMBORA rindo alto. Tu veste e volta. Nao goza, sua puta de auditório.",
|
||||
"🔥 EXPOSTO CAMINHADA HOMEM — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Um HOMEM aparece, TE VE, e ABRE um sorriso. 'Gostei da iniciativa.' Ele CHEGA PERTO e COLOCA a mão na tua nuca. 'VOCE É MEU AGORA.' Ele TE VIRA de costas e ENCOSTA teu corpo nu numa arvore. Ele MANDA: 'ABRE AS PERNAS E MOSTRA ESSE CU.' Tu abre e ele OLHA. Depois ele dá UM TAPA na tua bunda e diz: 'PODE IR, PUTA.' Tu veste sem olhar pra tras. Nao goza, seu puto dominado pelo estranho.",
|
||||
"🔥 EXPOSTO BIKE PEGO 6 — Vai de BIKE PELADO. Um GRUPO DE CICLISTAS aparece na direcao oposta. Eles TE VEEM e ALGUNS ASSOBIIAM. Um deles GRITA: 'OLHA O PELADO NA BIKE!' Eles PARAM e UM DELES MANDA: 'DESCE PRA GENTE VER DIREITO.' Tu desce da bike. Eles RODEAM tu pelado — 4 ciclistas te olhando. Um deles MANDA: 'ENCOSTA A BIKE NA ARVORE E SE CURVA, MOSTRA A BUNDA.' Tu faz isso e eles OLHAM. 'ESSAS NÁDEGAS MERECEM UM TAPA.' Um deles DA UM TAPA na tua bunda. Todos riem. Depois eles montam e vao embora. Tu monta e volta. Nao goza, seu puto rodeado.",
|
||||
"🔥 EXPOSTO BIKE PEGO 7 — Vai de BIKE PELADO. Um casal de IDOSOS esta num BANCO na praca e TE VEEM passando. A IDOSA TAPA a boca de surpresa mas CONTINUA olhando. O IDOSO MANDA: 'MOCINHO, PARAI AI.' Tu para a bike. 'ISSO É LUGAR DE ANDAR PELADO?' Tu fala que ta se exercitando. A IDOSA DIZ: 'BEM QUE PODIA MOSTRAR ESSAS PERNAS.' O idoso MANDA: 'QUE SEJA, DA UMA VOLTA AQUI PRA GENTE VER SE CÊ TEM VERGONHA.' Tu caminha pelado 30 segundos na frente deles. Eles COCHICHAM e depois VÃO EMBORA. Tu monta e volta. Nao goza, seu puto julgado pelos idosos.",
|
||||
"🔥 EXPOSTO CÚ PEGO JANELA — Vai ate a janela da COZINHA, abre. Tira TUDO da cintura pra baixo. Fica de QUATRO, CU escancarado pra janela. O ENTREGADOR passa na rua e TE VE. Ele PARA o carro, DESCE e GRITA: 'E ESSA MERCADORIA NA JANELA?' Tu nao se mexe. Ele MANDA: 'ABRE BEM ESSE CU QUE EU QUERO VER.' Tu abre com os dedos e mostra o interior. Ele OLHA 1 minuto e depois GRITA: 'APROVADO!' Entra no carro e vai embora. Tu fecha a janela. Nao goza, sua puta inspecionada pelo entregador.",
|
||||
"🔥 EXPOSTO CÚ PEGO PORTA — Vai ate a PORTA DA COZINHA, abre. Tira TUDO. Fica de QUATRO de FRENTE pra porta aberta, cu escancarado pro corredor. O SINDICO aparece no corredor e TE VE. 'QUE ABSURDO!' Ele CORRE ate a porta. 'TA MALUCO, VAI FICAR MOSTRANDO ISSO?' Tu imovel. Ele MANDA: 'LEVANTA AGORA E VAI PRA DENTRO.' Tu levanta e ele VE teu pau duro. Ele BALANCA a cabeca e vai embora resmungando. Tu fecha a porta. Nao goza, seu puto enquadrado.",
|
||||
"🔥 EXPOSTO CÚ PEGO MURO — Vai ate o QUINTAL, tira TUDO. Fica de QUATRO, cu pro muro. O VIZINHO está no quintal dele e VE teu cu por cima do muro. 'TA DANDO O CU PRO MUNDO?' Ele CHEGA PERTO do muro. 'ABRE COM A MÃO QUE EU QUERO VER.' Tu abre o cu com os dedos. Ele OLHA fixo. 'ENFIA UM DEDO.' Tu enfia o dedo no cu enquanto ele observa. 1 minuto de dedo no cu pro vizinho. Ele da UM GRUNHIDO de aprovação e volta pro que tava fazendo. Tu sai de quatro e veste. Nao goza, sua puta do vizinho.",
|
||||
"🔥 EXPOSTO CÚ PEGO CAMINHADA — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO 2 minutos. PARA e FICA de QUATRO, cu exposto. Uma MULHER aparece na trilha e TE VE de quatro. 'Nossa, que visão!' Ela CHEGA PERTO e SE AGACHA atras de ti. 'POSSO OLHAR?' Tu acena. Ela OLHA teu cu de perto. 'ABRE COM AS MÃOS.' Tu abre o cu pra ela. Ela ASSOPRA no teu cu — vento gelado. Depois LEVANTA e diz: 'BOA SORTE, PUTA.' e vai embora. Tu levanta, veste e volta. Nao goza, sua puta de trilha.",
|
||||
"🔥 EXPOSTO CÚ PEGO BIKE — Vai de BIKE PELADO pra lugar DESERTO. DESCE da bike e FICA de QUATRO, cu pro mundo. Um HOMEM de CAVALO aparece e TE VE. 'CARALHO, QUE CENA!' Ele DESCE do cavalo e se APROXIMA. 'CÊ TA DE QUATRO PRO MUNDO, HEIN?' Tu imovel. Ele MANDA: 'ENFIA O DEDO NO CU E MOSTRA PRA MIM.' Tu enfia o dedo e abre. Ele OLHA, monta no cavalo e vai embora. Tu levanta e monta na bike. Nao goza, sua puta de campo.",
|
||||
"🔥 EXPOSTO BUNDA PEGO AREA — Vai ate a AREA DE SERVICO, abre a porta. Tira calça e cueca. Debruça na MAQUINA de COSTAS, bunda pro alto. Um HOMEM aparece no corredor e TE VE de bunda pro alto. 'QUE BUNDA GOSTOSA.' Ele SE APROXIMA e PASSA a mão na tua bunda — uma mão estranha te tocando. 'DELICIA.' Tu imovel. Ele APERTA as duas nádegas e BALANÇA. 'BALANÇA PRA MIM.' Tu balança a bunda 10 vezes enquanto ele olha. Depois ele DA UM TAPA e vai embora. Tu fecha a porta e massageia a bunda. Nao goza, seu puto apalpado.",
|
||||
"🔥 EXPOSTO BUNDA PEGO MURO — Vai ate o QUINTAL, tira calça e cueca. Fica em PÉ de COSTAS pro muro, bunda nua. O VIZINHO aparece e TE VE. 'ISSO É CONVITE?' Ele ENCOSTA no muro e COLOCA a mão na tua bunda por cima do muro. PASSA a mão nas nádegas. 'TA BEM GOSTOSA.' Ele MANDA: 'CURVA PRA MIM.' Tu se curva e ele passa a mão na bunda toda. Depois ele tira a mão e vai embora. Tu veste e volta. Nao goza, sua puta apalpada pelo vizinho.",
|
||||
"🔥 EXPOSTO CABEÇA PEGO JANELA — Vai ate a janela, abre. Tira a camiseta, fica com o pau pra fora da calça. Passa LUBRIFICANTE na CABECINHA e alisa. Uma PASSANTE na rua TE VE alisando a cabecinha na janela. ELA PARA e OLHA. 'TA SE EXIBINDO, GOSTOSO?' Tu continua alisando. ELA MANDA: 'ALISA BEM DEVAGAR PRA EU VER.' Tu alisa lentamente a cabecinha em círculos. 1 minuto de show. Depois ela MANDA: 'MOSTRA A BUNDA AGORA.' Tu vira e mostra a bunda. Ela DA RISADA e vai embora. Tu fecha a janela. Nao goza, sua puta da janela.",
|
||||
"🔥 EXPOSTO CABEÇA PEGO BIKE — Vai de BIKE PELADO pra lugar DESERTO. APLICA lubrificante na CABECINHA e alisa, sentado na bike. Um CAMINHÃO para e o MOTORISTA TE VE alisando a cabecinha. 'QUE ISSO, TA SE CUIDANDO AÍ?' Tu para de alisar. Ele MANDA: 'CONTINUA QUE EU TO OLHANDO.' Tu alisa a cabecinha na frente dele. 'MAIS RÁPIDO.' Tu acelera. 'AGORA PARA E APONTA PRA MIM.' Tu aponta o pau pra ele, cabecinha brilhando. Ele da risada e vai embora. Tu monta e volta. Nao goza, sua puta da estrada.",
|
||||
"🔥 EXPOSTO CABEÇA PEGO CAMINHADA — Vai pra lugar DESERTO, tira TUDO. Passa lubrificante na CABECINHA e CAMINHA alisando. Uma MULHER aparece na trilha e TE VE alisando o pau. 'QUE ISSO?' Ela CHEGA PERTO. 'TA SE TOCANDO NO MEIO DO MATO?' Ela OLHA a cabecinha brilhando. 'DEIXA EU VER DE PERTO.' Ela se AGACHA e OLHA a cabecinha. 'POSSO ENCOSTAR?' Tu acena. Ela ENCOSTA a ponta do dedo na cabecinha e da risadinha. 'É BEM SENSÍVEL, HEIN?' Depois LEVANTA e vai embora. Tu veste e volta. Nao goza, sua puta tocada pela estranha.",
|
||||
"🔥 EXPOSTO CABEÇA PEGO QUINTAL — Vai ate o QUINTAL, tira a roupa da cintura pra baixo. Passa lubrificante na CABECINHA e alisa. O VIZINHO aparece e TE VE. 'TA PASSANDO CREME NO PAU, É?' Tu continua. ELE MANDA: 'ENCOSTA NO MURO E MOSTRA A CABECINHA PRA MIM.' Tu encosta a barriga no muro, pau apontando pra cima e alisa a cabecinha pra ele ver. Ele OLHA e diz: 'BONITA.' Depois se MANDA. Tu veste e volta. Nao goza, seu puto.",
|
||||
"🔥 EXPOSTO NU PEGO QUINTA — Vai ate o QUINTAL da FRENTE. Tira TUDO. Fica em PÉ ao lado do portão, totalmente nu. O CARRO DO VIZINHO ENTRA na rua e TE VE. Ele PARA O CARRO e FICA OLHANDO. 'CÊ TA PELADO NO QUINTAL, MACHO?' Tu imovel. Ele MANDA: 'ABRE O PORTÃO E VEM ATÉ O PORTÃO.' Tu abre e vai ate o portão, nu pro mundo. Ele OLHA de dentro do carro. 'DA UMA VOLTA PRA EU VER TUDO.' Tu gira completamente. Ele ASSOPIA e vai embora de carro. Tu fecha o portão. Nao goza, sua puta de portão.",
|
||||
"🔥 EXPOSTO NU PEGO RUA — Vai ate o PORTÃO da frente. Tira TUDO. ABRE uma FRESTA e COLOCA O PAU PRA FORA. Uma PESSOA PASSANDO NA RUA TE VE. 'Olha o esquisito!' ELA PARA. 'MOSTRA MAIS.' Tu abre mais a fresta e mostra o corpo nu. Ela OLHA e MANDA: 'SAI DO PORTÃO E MOSTRA TUDO.' Tu sai do portão, completamente nu, na frente da pessoa. 30 segundos de nudez total pro estranho. Ela OLHA, BALANÇA a cabeça e vai embora. Tu entra e fecha. Nao goza, sua puta sem vergonha.",
|
||||
"🔥 EXPOSTO BIKE PEGO GRUPO 2 — Vai de BIKE PELADO. Um GRUPO de jovens esta num ponto e TE VEEM passar. ELES GRITAM e CORREM ate tu. 'MANO, TU TA PELADO!' Eles RODEIAM a bike. Uma das GAROTAS MANDA: 'DESCE PRA GENTE VER DIREITO.' Tu desce. Outra MANDA: 'TIRA A CAMISETA TAMBÉM.' Tu tira a camiseta e fica totalmente nu no meio do grupo. Eles OLHAM, UM DELES PASSA a mão na tua bunda e DA UM TAPA. Riem todos. 'BOA SORTE, PUTA!' Eles vao embora. Tu monta e volta. Nao goza, sua puta de grupo.",
|
||||
"🔥 EXPOSTO BIKE PEGO MOTO — Vai de BIKE PELADO. Um MOTOQUEIRO para a MOTO DO LADO e TE VE. 'CARALHO, QUE CENA BOA!' Ele DESCE da moto. 'CÊ TA DE BIKE PELADO, VAI TOMAR UM BANHO?' Tu ri sem graça. Ele MANDA: 'DESCE DA BIKE E VEM AQUI.' Tu vai ate ele. Ele COLOCA a mão no teu ombro e TE EXAMINA. 'QUE CORPO, HEIN?' Ele PASSA a mão na tua bunda. 'VIRA DE COSTAS.' Tu vira. Ele OLHA e MANDA: 'ABRE AS PERNAS.' Tu abre e mostra o cu. Ele APROVA com a cabeca, monta na moto e vai embora. Tu monta na bike. Nao goza, seu puto inspecionado pelo motoqueiro.",
|
||||
"🔥 EXPOSTO BIKE PEGO OLEIRO — Vai de BIKE PELADO. Um CAMINHÃO basculante ou trator passa e o OLEIRO TE VE. Ele PARA a máquina e DESCE. 'E ESSA BIKE PELADA?' Ele CHEGA PERTO e OLHA. 'BONITO HEIN.' Ele MANDA: 'SENTA NO GUIDÃO.' Tu senta no guidão da bike, o pau escancarado pra ele. Ele OLHA e se aproxima. 'VOU DAR UM TRATO NISSO.' Ele ENCOSTA um dedo sujo de graxa na tua cabecinha. Sujo. Depois LIMPA na roupa dele e da risada. 'AGORA VOLTA PRA CASA E LAVA ESSA CABECINHA.' Monta no caminhão e vai. Nao goza, seu puto sujinho.",
|
||||
"🔥 EXPOSTO BIKE PEGO PESCADOR — Vai de BIKE PELADO pra lugar perto de RIO ou LAGOA. Um PESCADOR esta na margem e TE VE. 'PESQUEI UM PELADO!' Ele se LEVANTA e vem. 'QUE ISSO, TA PROCURANDO PEIXE?' Tu para. Ele OLHA teu pau. 'ISSO É UMA VARINHA, NÃO UM PEIXE.' Ele MANDA: 'VAI TOMAR UM BANHO NU NO RIO LÁ.' Tu vai ate a agua e ENTRA pelado na agua. O pescador OLHA da margem. 'AGORA SAI E MOSTRA O PEIXE SECANDO.' Tu sai e fica em pé na margem, pingando, pau duro e molhado. Ele OLHA e volta a pescar. Tu veste e vai. Nao goza, sua puta molhada.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO 4 — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Uma MULHER com UMA CRIANÇA (segura) aparecem. A mulher TE VE e RAPIDAMENTE tampa os olhos da criança. 'QUE ABSURDO!' Ela MANDA: 'VIRA DE COSTAS AGORA.' Tu vira. Ela PASSA com a criança e te xinga baixinho. Depois que elas passam, tu se VIRA e ve ela OLHANDO de volta furtivamente. Ela desvia o olhar e vai embora. Tu veste e volta rindo. Nao goza, sua puta quase pego pela família.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO 5 — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO COM AS MÃOS NA CABEÇA. Um HOMEM aparece e TE VE. 'QUE POSIÇÃO É ESSA?' Ele CHEGA PERTO. 'PARECE UM PRESO ANDANDO NU.' Ele MANDA: 'AJEELHA.' Tu ajoelha nu no chão, mãos na cabeça. Ele PASSA em volta te olhando. 'É ASSIM QUE PUTA ANDA?' Tu ajoelhado, imovel. 'LEVANTA E VAI EMBOORA.' Tu levanta e veste. Nao goza, seu puto submisso.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO 6 — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Duas MULHERES DE CAVALO aparecem na trilha. Elas TE VEEM e PARAM os cavalos. 'Nossa, que surpresa!' Uma DELAS DESCE do cavalo. 'CÊ TAVA ESPERANDO NÓS?' Ela PASSA o rebenque (ou mão) na tua bunda. 'QUE BUNDA.' A outra MANDA: 'CURVA E MOSTRA.' Tu se curva e elas OLHAM. 'REBOLA 10 PRA NÓS.' Tu rebola 10 vezes nu na trilha enquanto elas olham dos cavalos. Depois MONTAM e vao embora. Nao goza, sua puta de cavalaria.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO DOMINADO — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Um HOMEM de CAPUZ aparece e TE ABORDA. 'PERDEU A ROUPA?' Tu tenta falar. Ele MANDA: 'CALADO. MÃOS NAS COSTAS.' Ele TE AMARRA as mãos com um cinto ou corda. 'AGORA VAI ANDANDO NA MINHA FRENTE, NU, DE MÃOS PRESAS.' Ele te guia pelo braço e tu caminha pelado de mãos presas por 3 minutos. Ele para, te DESAMARRA e MANDA: 'VAI EMBOORA E NAO CONTA PRA NINGUEM.' Tu veste e sai rapidão. Nao goza, seu puto capturado.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO FISCAL — Vai pra lugar SEMI-DERTO. Tira TUDO. CAMINHA PELADO. Um GUARDA ou FISCAL aparece e TE VE. 'ISSO É PROIBIDO, VAI TER QUE ME ACOMPANHAR.' Tu treme. Ele CHEGA PERTO e OLHA teu corpo. 'MAS... PODE SER QUE EU DEIXE PASSAR SE VOCÊ COOPERAR.' Ele MANDA: 'CURVA E APOIA NAS MÃOS.' Tu faz isso. Ele PASSA a mão na tua bunda. 'BOA COOPERAÇÃO.' Ele da risada. 'PODE IR, MAS NA PRÓXIMA VOU MULTAR.' Tu veste e sai correndo. Nao goza, seu puto fiscalizado.",
|
||||
"🔥 EXPOSTO BIKE PEGO CAMINHAO — Vai de BIKE PELADO. Um CAMINHÃO BAU para e o MOTORISTA TE VE. 'EI, PELADO, VEM CA.' Tu para. Ele ABRE a porta do caminhão e MANDA: 'SOBE AÍ.' Tu SOBE no caminhão nu. Dentro do baú, tem UM COLCHÃO. 'DEITA.' Tu deita nu no colchão dentro do caminhão. Ele OLHA teu corpo, APALPA tua bunda, e MANDA: 'ABRE AS PERNAS.' Tu abre. Ele PASSA a mão no teu cu. 1 minuto de apalpação. Depois MANDA: 'PODE DESCER.' Tu desce do caminhão, monta na bike e vai embora. Nao goza, seu puto do baú.",
|
||||
"🔥 EXPOSTO BIKE PEGO CAMINHO — Vai de BIKE PELADO. Uma PESSOA numa PICKUP para e TE OLHA. 'QUE ISSO, TA PERDIDO?' Tu para. ELA MANDA: 'ENCosta A BIKE E VEM NO CARRO.' Tu vai. ELA PASSA a mão no teu pau e PERGUNTA: 'JÁ GOZOU HOJE?' Tu nega. ELA MANDA: 'ENTÃO VAI GUARDANDO.' Depois ela vai embora. Tu volta pra bike e pedala, frustrado. Nao goza, sua puta.",
|
||||
"🔥 EXPOSTO BIKE PEGO PARQUE — Vai de BIKE PELADO pra um PARQUE VAZIO. PEDALA dentro do parque. Um JARDINEIRO esta podando e TE VE. 'RAPAZ, CÊ TA PELADO NO PARQUE?' Ele ri. Ele MANDA: 'DA UMA VOLTA NO PARQUE INTEIRO PELADO.' Tu da uma volta completa no parque pelado enquanto ele assiste de longe. A cada passada, ele da risada. No final, ELE MANDA: 'AGORA VAI PRA CASA.' Tu monta e vai. Nao goza, sua puta de parque.",
|
||||
"🔥 EXPOSTO BIKE PEGO ESTRADA — Vai de BIKE PELADO numa ESTRADA de terra. Um TRATOR para e o TRATORISTA TE VE. 'QUE ISSO MULEQUE?' Ele para o trator e DESCE. 'TA NESTADO.' Ele OLHA teu corpo nu. 'BATE UMA PUNHETA AI PRA EU VER.' Tu masturba na frente dele. 'ATÉ QUASE GOZAR.' Tu quase goza e PARA. Ele da risada: 'BOIADEIRO APROVA.' Monta no trator e vai embora. Nao goza, seu puto da roça.",
|
||||
"🔥 EXPOSTO BIKE PEGO POSTO — Vai de BIKE PELADO e PARA num POSTO ou PONTO de onibus DESERTO. Uma MULHER esta no ponto esperando onibus e TE VE. 'MEU DEUS!' Ela OLHA. 'CÊ TA PELADO NA BIKE?' Tu acena. ELA PERGUNTA: 'POSSO?' e ENCOSTA a mão no teu pau. Tu deixa. Ela alisa a CABECINHA por 30 segundos. 'BONITO.' Depois o onibus chega e ela SOBE. Tu monta na bike e vai. Nao goza, sua puta do ponto.",
|
||||
"🔥 EXPOSTO BIKE PEGO PRAIA — Vai de BIKE PELADO ate uma PRAIA DESERTA. PEDALA na areia pelado. Uma PESSOA esta na praia e TE VE. ELA CORRE ate tu. 'NATURISMO, HEM?' Ela OLHA teu corpo nu e PERGUNTA: 'POSSO TIRAR UMA FOTO?' Tu acena. Ela TIRA FOTO do teu corpo nu na praia com a bike. DEPOIS MANDA: 'ENTRA NO MAR, FAZ UM STROKE.' Tu entra no mar e BRAÇA um pouco pelado enquanto ela olha. Depois sai e ELA VAI embora. Nao goza, sua puta fotografada na praia.",
|
||||
"🔥 EXPOSTO BIKE PEGO LAVRADOR — Vai de BIKE PELADO num CAMINHO DE ROÇA. Um LAVRADOR NA ENXADA TE VE. 'RAPAZ, TA NU?' Ele ENCOSTA a enxada e VEM. 'ISSO É LÁ O QUÊ?' Ele OLHA teu pau. 'TA DURO AINDA.' Ele MANDA: 'SE VIRA DE COSTAS E MOSTRA A RAPEIRA.' Tu vira e mostra o cu. Ele OLHA e da risada: 'TA BONITO.' Ele da UM TAPA de leve e VOLTA pro trabalho. Nao goza, seu puto da lavoura.",
|
||||
"🔥 EXPOSTO BIKE PEGO PEDREIRO — Vai de BIKE PELADO. Passa numa OBRA e os PEDREIROS te veem. ASSOBIOS e GRITOS. 'OLHA O PELADO!' Um deles DESCE e TE ABORDA. 'TA PROCURANDO SERVIÇO?' Tu acena que nao. Ele MANDA: 'DESCE DA BIKE E MOSTRA O SERVIÇO.' Tu desce. 'ABRE AS PERNAS.' Tu abre. Ele OLHA e MANDA: 'DA 10 AGACHAMENTOS PELADO AQUI.' Tu faz 10 agachamentos no meio da rua enquanto os pedreiros olham e riem. 'APROVADO!' Tu monta e foge. Nao goza, sua puta da obra.",
|
||||
"🔥 EXPOSTO UNICO PEGO PORTA — Vai ate a PORTA DE ENTRADA, abre ela. Tira TUDO e FICA PELADO na porta de casa, olhando pra rua. UM CARRO para em frente e uma MULHER DESCE. 'PERDEU A CHAVE?' Ela ri. Ela SE APROXIMA e PASSA a mão no teu peito nu. 'QUE PEITÃO.' Ela ENCOSTA a mão no teu pau. 'DELICIA.' Ela MANDA: 'ENTRA PRA DENTRO, QUE EU VOU AÍ.' Tu entra, mas ELA VAI EMBORA de carro, rindo. Tu fecha a porta. Nao goza, sua puta que ficou esperando.",
|
||||
"🔥 EXPOSTO UNICO JARDIM — Vai ate o JARDIM ou QUINTAL DA FRENTE. Tira TUDO. Fica CURVADO REGANDO as plantas PELADO — mangueira na mão, pau balançando. A VIZINHA passa e TE VE. 'CUIDANDO DO JARDIM?' Ela PARA e OLHA. 'QUE MANGUEIRÃO.' Ela PERGUNTA: 'POSSO AJUDAR?' Tu acena. Ela PEGA a mangueira e DIRECIONA O JATO pra teu pau — agua fria na cabecinha. 'BATE UMA LAVAGEM BEM.' Tu alisa a cabecinha com agua fria. Ela ri e vai embora. Nao goza, seu puto molhado.",
|
||||
"🔥 EXPOSTO BIKE PEGO CICLISTA — Vai de BIKE PELADO. Outro CICLISTA aparece e TE VE. 'RAPAZ, TA PELADO!' Ele PARA e DESCE da bike. 'ISSO É CORAGEM.' Ele OLHA teu pau. 'DEIXA EU TIRAR UMA FOTO PRA MOSTRAR PROS AMIGOS?' Tu acena que nao. Ele INSISTE: 'ENTAO MOSTRA ESSA BUNDA PRA EU CONTAR.' Tu vira e mostra a bunda. Ele OLHA, da risada, monta na bike e vai. Nao goza, seu puto.",
|
||||
"🔥 EXPOSTO BIKE PEGO MECANICO — Vai de BIKE PELADO. A corrente da bike CAÍ e tu precisa EMPURRAR a bike ate um lugar. Um MECANICO numa OFICINA TE VE empurrando a bike pelado. 'PRECISA DE AJUDA?' Tu fala que sim. Ele OLHA teu corpo nu enquanto CONSERTA a corrente. 'VOU COBRAR CARO.' Ele TERMINA e MANDA: 'MOSTRA O PAGAMENTO.' Tu mostra o pau. Ele PEGA um PANO SUJO e PASSA na cabecinha — oleo e graxa. 'AGORA TA PAGO.' Tu monta e vai. Nao goza, seu puto sujo.",
|
||||
"🔥 EXPOSTO BIKE PEGO VENDEDOR — Vai de BIKE PELADO. Um VENDEDOR AMBULANTE esta na rua e TE VE. 'COMPRA ALGUMA COISA?' Tu para. Ele OLHA teu corpo nu. 'VOU TE DAR UM NEGOCIO.' Ele da UM PRESERVATIVO pra tu. 'COLOCA.' Tu coloca o preservativo no pau DURO. Ele MANDA: 'AGORA PEDALA ASSIM, PUTA DE CAMISINHA.' Tu pedala 3 minutos pelado de camisinha na frente dele, que da risada. Depois tira e guarda. Nao goza, sua puta de camisinha.",
|
||||
"🔥 EXPOSTO BIKE PEGO GUARDA — Vai de BIKE PELADO. Um CARRO DE POLICIA aparece e te ABORDA. 'ENCOS-TA.' Tu para, nervoso. O POLICIAL DESCE. 'CÊ TA PELADO NA VIA PÚBLICA.' Tu treme. Ele OLHA teu corpo. 'VOU TER QUE REVISTAR.' Ele PASSA a mão nas tuas pernas, na tua bunda, nos teus ovos. 'REVISTA COMPLETA.' Depois da risada. 'PODE IR, MAS DA PRÓXIMA VOU MULTAR.' Tu monta e pedala o mais rápido possivel. Nao goza, seu puto revistado pela autoridade.",
|
||||
"🔥 EXPOSTO BIKE PEGO MULHER — Vai de BIKE PELADO. Uma MULHER num CARRO ESPORTE passa, REDUZ e te OLHA. Ela ABRE a janela e GRITA: 'QUE CORPO!' Ela ESTACIONA e DESCE. Divina, salto alto, vestido curto. Ela CHEGA e ENCOSTA em ti. 'QUER UMA CARONA?' Tu acena que nao. Ela PASSA a mão na tua bunda e PERGUNTA: 'JÁ COMERAM VOCÊ HOJE?' Tu nega. Ela MANDA: 'ENTÃO FICA ASSIM, GUARDANDO ESSE LEITE.' Depois VOLTA pro carro e vai embora. Tu monta e pedala louco. Nao goza, sua puta provocada.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO MOTOQUEIRO — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Uma MOTO passa e VOLTA. 'QUE CENA BOA!' O MOTOQUEIRO DESCE da moto e se APROXIMA. 'NUNCA VI UM PELADO NA ESTRADA.' Ele OLHA teu corpo. 'DEIXA EU TIRAR UMA FOTO.' Tu recusa. Ele MANDA: 'ENTAO MOSTRA O CU QUE EU VOU PRA CASA FELIZ.' Tu vira e mostra a bunda. Ele OLHA, da risada, monta na moto e vai embora cantando pneu. Nao goza, seu puto de estrada",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO FITNESS — Vai pra lugar DESERTO, tira TUDO. COMECA a fazer EXERCICIOS pelado — 5 polichinelos, 5 agachamentos. Uma PESSOA CORRENDO aparece e TE VE se exercitando nu. ELA PARA. 'QUE TREINO É ESSE?' Ela ri. 'MOSTRA COMO FAZ O AGACHAMENTO DIREITO.' Tu faz 5 agachamentos na frente dela, o pau apontando. Ela AVALIA: 'FORMA BOA.' Ela CORRE e vai embora. Tu veste e vai. Nao goza, sua puta fitness.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO DOMADOR — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Um HOMEM aparece e TE DOMINA. 'MÃOS NO CHÃO.' Tu obedece. Ele COLOCA uma CINTA ou CORDA no teu pescoço como se fosse uma coleira. 'AGORA VAI ANDAR DE QUATRO NA MINHA FRENTE.' Tu anda de quatro pelado, com a corda no pescoço, guiado por ele por 3 minutos. Ele PARA, TIRA a corda e MANDA: 'PODE IR, CACHORRO.' Tu veste e sai. Nao goza, seu puto dominado.",
|
||||
"🔥 EXPOSTO CAMINHADA PEGO MESTRE — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Um HOMEM VELHO (ou SENHORA) aparece e TE VE. 'QUE JOVEM OUSADO.' Ele/A ELA SE APROXIMA. 'VOCÊ PRECISA DE DISCIPLINA.' Ele MANDA: 'AJÖELHE.' Tu ajoelha. Ele PASSA a mão na tua cabeca. 'REPITA: SOU UMA PUTA.' Tu repete. 'MAIS ALTO.' Tu grita: 'SOU UMA PUTA!' Ele APROVA e diz: 'PODE IR.' Tu veste e volta. Nao goza, seu puto disciplinado.",
|
||||
"🔥 EXPOSTO BIKE PEGO ENCONTRO — Vai de BIKE PELADO pra um PONTO DE ENCONTRO marcado (lugar deserto). Uma PESSOA ja TE ESPERA LA. Ela TE VE chegando pelado na bike. 'AÍ SIM, PONTUAL.' Ela ABRAÇA tu nu. PASSA a mão no teu corpo. 'VALEU A PENA.' Ela MANDA: 'AGORA VAI DAR UMA VOLTA PELADA PRA EU VER.' Tu faz 2 voltas de bike pelado ao redor dela enquanto ela olha. Depois ELA CHAMA e tu PARA. 'PODE IR, PUTA.' Tu volta pra casa. Nao goza, seu puto pontual.",
|
||||
"🔥 EXPOSTO BIKE PEGO FÃ — Vai de BIKE PELADO. Uma PESSOA ja te viu outras vezes e TE RECONHECE. 'É O PELADO DA BIKE!' Ela CORRE ate tu. 'TO TE SEGUINDO.' Ela PERGUNTA: 'POSSO FILMAR?' Tu acena que sim. ELA FILMA 1 minuto da tua bike pelado. 'MANDA UM SALVE.' Tu manda um salvo pelado. 'VAI VIRAR VIRAL.' Tu monta e vai. Nao goza, sua puta filmada.",
|
||||
"🔥 EXPOSTO BIKE PEGO AMIGO — Vai de BIKE PELADO. De repente UM AMIGO TE VE e RECONHECE. 'RAPAZ, É VOCÊ?' Tu morre de vergonha. Ele CHEGA PERTO e da risada. 'CÊ TA PELADO.' Ele OLHA teu pau. 'CARALHO.' Ele MANDA: 'VIRA PRA EU VER O CU.' Tu vira e ele OLHA. 'AMIGO, TU TA LOCO.' Ele BALANÇA a cabeça e vai embora rindo. Tu monta a bike e voa pra casa. Nao goza, seu puto queimado pelo amigo.",
|
||||
"🔥 EXPOSTO BIKE PEGO VIZINHA 2 — Vai de BIKE PELADO. A VIZINHA ta na janela e TE VE PASSAR. 'NOVAMENTE?' Ela ABRE a janela. 'VEM AQUI.' Tu vai ate a janela. Ela OLHA teu corpo na bike. 'QUE CORAGEM.' Ela MANDA: 'SENTA NO GUIDÃO QUE EU QUERO VER.' Tu senta no guidão, pau escancarado. Ela OLHA e PERGUNTA: 'JÁ BATEU UMA HOJE?' Tu nega. 'GUARDA ENTÃO, VOU PENSAR EM TI.' Ela fecha a janela. Tu pedala pra casa. Nao goza, sua puta da vizinha.",
|
||||
"🔥 EXPOSTO BIKE PEGO CASA — Vai de BIKE PELADO e PASSA em frente de casa. A PORTEIRO ou CASEIRO TE VE. 'RAPAZ, QUE É ISSO?' Ele CORRE. 'TA LOCO.' Ele TE EMPURRA pra dentro do portão. 'VESTE ISSO.' Ele te da uma camiseta. 'NADA DE BIKE PELADA.' Ele BALANÇA a cabeça. Tu veste e entra. Nao goza, seu puto enquadrado.",
|
||||
"🔥 EXPOSTO TRANSPORTE PEGO — Vai de BIKE PELADO e EMPURRA a bike por um trecho. Um CAMINHÃO DE GÁS para e o ENTREGADOR TE VE. 'PRECISA DE UMA MÃO?' Tu aceita. Ele PEGA tua bike e COLOCA na carroceria. 'SOBE.' Tu SOBE pelado na carroceria do caminhão. Ele DA UMA CARONA de 2 minutos em pé na carroceria, nu, a brisa no corpo. Ele PARA e MANDA: 'DESCE.' Tu desce com a bike e ele vai embora. Nao goza, sua puta de carona.",
|
||||
"🔥 EXPOSTO BIKE PEGO FINAL — Vai de BIKE PELADO pra um lugar VISÍVEL, PARQUE ou PRAÇA VAZIA. FICA SENTADO na bike pelado, PERNAS ABERTAS, por 5 MINUTOS. Se ALGUÉM PASSAR e TE VER, tu CONTINUA imóvel mostrando TUDO. Se ALGUÉM PARAR e TE OLHAR, tu ACENA. 5 minutos de exposição máxima no espaço público. Depois monta e volta. Nao goza, sua puta final.",
|
||||
"🔥 EXPOSTO PEGO BANCO PRAÇA — Vai pra uma PRAÇA ou PARQUE DESERTO. Tira TUDO. SENTA NUM BANCO PELADO, de pernas abertas, o pau DURO apontando pro espaço. Uma MULHER com UM CACHORRO passa e TE VE. Ela SOLTA a coleira sem querer e o CACHORRO VEM cheirar teu pau. ELA CORRE: 'MEU DEUS, DESCULPA!' Ela PEGA o cachorro e OLHA de novo. 'ELE GOSTOU.' Ela da risada e vai embora. Tu veste e vai. Nao goza, seu puto cheirado pelo cachorro.",
|
||||
"🔥 EXPOSTO PEGO POSTE — Vai pra uma RUA DESERTA a noite (com cuidado). Tira TUDO e FICA EM PÉ abraçado num POSTE de luz, o corpo iluminado. Um CARRO passa devagar e o MOTORISTA TE VE. Ele PARA. 'QUE LUZ BOA.' Ele DESCE, te OLHA contra o poste, e MANDA: 'CURVA NO POSTE.' Tu se curva no poste, bunda exposta. Ele PASSA a mão na tua bunda e GRUNHE. 'DELICIA.' Depois VOLTA pro carro e vai. Tu veste e volta. Nao goza, seu puto iluminado.",
|
||||
"🔥 EXPOSTO PEGO OBRA — Vai pra uma CONSTRUÇÃO ou OBRA ABANDONADA. Tira TUDO e CAMINHA pelos corredores pelado. Um OPERÁRIO aparece e TE VE. 'O QUE É ISSO?' Ele ri. 'FANTASMA DE OBRA.' Ele MANDA: 'SENTA NA LAJE E MOSTRA.' Tu senta na laje, pernas abertas. Ele OLHA de longe e GRITA: 'APROVADO! MAS VISTE ANTES DE ALGUÉM TE VER.' Tu veste e sai. Nao goza, seu puto da obra.",
|
||||
"🔥 EXPOSTO PEGO TÚNEL — Vai pra um TÚNEL ou PASSAGEM SUBTERRÂNEA DESERTA. Tira TUDO. CAMINHA PELADO no tunel, o eco dos passos. Uma PESSOA ENTRA no tunel e TE VE de longe. ELA PARA. 'QUE VISÃO.' Ela SE APROXIMA e OLHA teu corpo nu na penumbra. 'É UM ANJO?' Ela ENCOSTA a mão no teu peito. 'UM ANJO PELADO.' Ela MANDA: 'VIRA PRA EU VER AS ASAS.' Tu vira e mostra a bunda. Ela ASSOPIA. 'BELAS ASAS.' Depois ELA VAI embora. Tu veste e sai. Nao goza, seu puto angelical.",
|
||||
"🔥 EXPOSTO PEGO ESCADARIA — Vai pra uma ESCADARIA ou DEGRAUS DESERTOS. Tira TUDO. SENTA NOS DEGRAUS PELADO, pernas abertas. Uma PESSOA SOBE a escadaria e TE VE de baixo pra cima. 'ISSO É CONVITE?' Ela SOBE MAIS e PARA na tua frente. 'QUE VISTA.' Ela COLOCA a mão no teu ombro. 'POSSO SENTAR?' Tu cede. Ela SENTA DO TEU LADO e OLHA teu pau. Depois LEVANTA e SOBE o resto da escada. Lá de cima, MANDA: 'BOA SORTE, PUTA.' Tu veste e vai. Nao goza, seu puto de escadaria.",
|
||||
"🔥 EXPOSTO PEGO MURO SENTADO — Vai ate um MURO ou CERCA. Tira TUDO e SENTA EM CIMA DO MURO PELADO, pernas abertas, pau pro mundo. Um VIZINHO TE VE do outro lado. 'RAPAZ, CÊ QUER MORRER?' Ele ri. 'VOCÊ É OUSADO.' Ele MANDA: 'FICA AI MAIS 1 MINUTO QUE EU VOU CHAMAR OS AMIGOS.' Tu fica 1 minuto no muro e depois PULA pra dentro. Ele GRITA: 'FUJÃO!' Tu veste e corre. Nao goza, seu puto do muro.",
|
||||
"🔥 EXPOSTO PEGO VARAL — Vai ate a AREA DE SERVIÇO, tira TUDO e ESTENDE ROUPA pelado. O VIZINHO aparece e TE VE estendendo roupa nu. 'TA FAZENDO A LAVANDERIA?' Ele ri. 'FALTAU A ROUPA?' Tu continua estendendo. Ele OLHA e MANDA: 'ESTENDE A MINHA CAMISA TAMBÉM.' Tu estende a camisa dele pelado, alcançando o varal, o pau esticando. Depois ele ri e vai embora. Nao goza, seu puto lavadeiro.",
|
||||
"🔥 EXPOSTO PEGO CHURRAS — Vai pro QUINTAL e ACENDE a CHURRASQUEIRA. Tira TUDO. ASSA pelado a carne, o pau balançando, a fumaça no corpo. O VIZINHO SENTE O CHEIRO e OLHA por cima do muro. 'TA ASSANDO PELADO?' Ele ri. 'ESSA CARNE VAI TER GOSTO DE PUTA.' Ele MANDA: 'ME TRAZ UM PEDAÇO.' Tu leva um pedaço ate o muro pelado. Ele OLHA teu corpo, PEGA a carne e MANDA: 'BOA, MAS VESTE ANTES DE QUEIMAR O PAU.' Tu volta pra churrasqueira. Nao goza, seu puto churrasqueiro.",
|
||||
"🔥 EXPOSTO PEGO PORTÃO CARRO — Vai ate o PORTÃO da FRENTE e FICA PELADO atras do portão, segurando as grades. Um CARRO PARA e o MOTORISTA TE VE. 'É UM ANIMAL NA JAULA?' Ele DESCE e CHEGA PERTO. 'QUE JAULA BONITA.' Ele ENCOSTA a mão entre as grades e PASSA na tua bunda. 'BEM TRATADO.' Ele MANDA: 'ABRE O PORTÃO.' Tu abre. Ele ENTRA e OLHA teu corpo. 'VOU DAR UMA OLHADA NO SISTEMA.' Ele PASSEIA o olhar no teu corpo nu, 1 minuto. Depois SAI e MANDA: 'PODE FECHAR.' Vai embora. Nao goza, seu puto inspecionado.",
|
||||
"🔥 EXPOSTO PEGO CORREIO — Vai ate o PORTÃO, abre, e FICA PELADO na CALÇADA. O CARTEIRO aparece e TE VE. 'SEDEX?' Ele ri. TENHO UMA ENCOMENDA PRA TI.' Ele te da um ENVELOPE. Tu PELADO recebendo correspondência. Ele OLHA e PERGUNTA: 'VAI ME CONVIDAR PRA ENTRAR?' Tu acena que nao. Ele MANDA: 'ENTAO ASSINA AQUI.' Tu assina pelado. Ele PEGA, da risada e vai embora. Nao goza, seu puto do correio.",
|
||||
"🔥 EXPOSTO PEGO CAMINHÃO DE LIXO — Vai ate a RUA, tira TUDO e FICA PELADO perto da lixeira. O CAMINHÃO DE LIXO passa e o GARIS TE VE. 'OLHA O LIXO!' Eles GRITAM e ASSOBIAM. 'VAMOS LEVAR?' Um deles DESCE e CHEGA PERTO. 'ISSO É LIXO RECICLAVEL?' Ele PASSA a mão na tua bunda. 'BEM CONSERVADO.' Ele VOLTA pro caminhão e MANDA: 'GUARDA ESSE LIXO PRA COLETA NOTURNA.' Vao embora rindo. Tu veste e entra. Nao goza, seu puto do lixo.",
|
||||
"🔥 EXPOSTO PEGO TIO — Vai ate a casa do TIO ou PARENTE (se for viavel). BATE na porta. Quando ele ABRE, tu TA PELADO. 'MEU DEUS, SOBRINHO!' Ele ri. 'QUE VISITA É ESSA?' Ele OLHA teu corpo. 'TÁ DE SACANAGEM?' Tu fala que perdeu a roupa no banho. Ele MANDA: 'ENTRA LOGO ANTES QUE A VIZINHA VEJA.' Tu entra pelado. Ele da uma CAMISA. 'VESTE, PUTA.' Tu veste e vai embora. Nao goza, seu puto sem vergonha na familia.",
|
||||
"🔥 EXPOSTO PEGO CACHORRO — Vai ate o QUINTAL, tira TUDO. Fica de QUATRO no quintal, cu exposto. O CACHORRO DO VIZINHO entra no quintal e TE CHEIRA o cu — o focinho frio no teu cu. Tu imovel. O VIZINHO aparece e CHAMA o cachorro. 'O QUE CÊ TA FAZENDO?' Ele TE VE de quatro. 'MEU CACHORRO GOSTOU.' Ele ri e MANDA: 'FICA AI MAIS 1 MINUTO QUE ELE VOLTA.' Tu fica. O cachorro volta e cheira de novo. Depois o vizinho CHAMA e vai embora. Tu veste e entra. Nao goza, seu puto cheirado.",
|
||||
"🔥 EXPOSTO PEGO BICHO — Vai pra lugar DESERTO, tira TUDO. CAMINHA PELADO. Uma VACA ou CAVALO aparece e TE VE. O ANIMAL CHEGA PERTO e TE CHEIRA — o focinho quente na tua barriga, no teu pau. Tu imovel. O ANIMAL LAMBE teu ombro. O FAZENDEIRO aparece e GRITA: 'QUE ISSO?' O homem CORRE e TE VE pelado sendo lambido pela vaca. 'RAPAZ, A VACA TE ADOTOU.' Ele ri. DESCE do cavalo (ou chega) e PERGUNTA: 'CÊ TA BEM?' Tu acena. Ele BALANÇA a cabeca e MANDA: 'VESTE LOGO ANTES QUE O TOURO TE PEGUE.' Tu veste e sai. Nao goza, seu puto do campo.",
|
||||
"🔥 EXPOSTO PEGO PIPA — Vai pra um CAMPO aberto. Tira TUDO e SOLTA UMA PIPA pelado. Uma CRIANÇA (imaginaria ou voce sozinho) — na verdade uma PESSOA aparece e TE VE soltando pipa pelado. 'PIPA LEGAL.' A pessoa OLHA teu corpo. 'VOCÊ É O PIPA.' Tu ri. A pessoa MANDA: 'DA UMA CORRIDA COM A PIPA PELADO.' Tu corre pelado no campo com a pipa subindo. O vento no corpo nu, a pipa no ceu. 2 minutos de corrida nua. A pessoa OLHA e APPLAUDE. 'BOM ESPETACULO.' Vai embora. Naoo goza, seu puto soltando pipa.",
|
||||
"🔥 EXPOSTO PEGO PADEIRO — Vai ate uma PADARIA ou MERCADO PERTO (se for seguro, de bike). Vai de BIKE PELADO e FICA NA CALÇADA em frente. O PADEIRO TE VE e SAÍ. \"É CLIENTE?\" Ele OLHA teu corpo nu. \"VOU TE SERVIR.\" Ele da UM PÃO QUENTE pra tu segurar pelado. \"COME.\" Tu come pelado na calcada. Ele OLHA e VOLTA pra dentro. Nao goza, seu puto do pao.",
|
||||
"🔥 EXPOSTO PEGO PEDRA — Vai ate uma PEDRA GRANDE ou ROCHA em lugar deserto. Tira TUDO. SENTA NA PEDRA PELADO, pernas abertas. Uma PESSOA aparece e TE VE sentado na pedra. \"É UMA ESTÁTUA?\" Ela CHEGA PERTO e OLHA. \"UMA ESTATUA DE PUTA.\" Ela PASSA a mão no teu pau. \"MUITO REALISTA.\" Ela MANDA: \"FICA IMÓVEL MAIS 1 MINUTO.\" Tu fica imóvel enquanto ela contorna. Depois vai embora. Nao goza, seu puto estátua.",
|
||||
"🔥 EXPOSTO PEGO PONTE — Vai ate uma PONTA ou PONTE em lugar deserto. Tira TUDO e FICA DE QUATRO na ponta da ponte, cu pro abismo. Um PESCADOR (ou passante) TE VE e GRITA: \"PESCA GROSSA!\" Ele CHEGA. \"QUE ISSO, TA PESCANDO DE CU?\" Ele MANDA: \"BALANÇA O CU QUE O PEIXE MORDE.\" Tu balança 10 vezes. Ele OLHA e vai embora rindo. Nao goza, seu puto pescador.",
|
||||
"🔥 EXPOSTO PEGO POSTO 2 — Vai ate um POSTO DE GASOLINA deserto a noite. Tira TUDO e FICA DO LADO DA BIKE, abastecendo imaginariamente. O FRENTISTA TE VE. \"OI, VAI COMPLETAR?\" Ele OLHA teu corpo. \"O TANQUE É ONDE?\" Ele MANDA: \"ABRE A TAMPA.\" Tu aponta pro cu. Ele ri e MANDA: \"PODE IR, PUTA.\" Nao goza, seu puto do posto.",
|
||||
"🔥 EXPOSTO PEGO CAMPING — Vai ate um local de CAMPING DESERTO (se possivel). Monta UMA BARRACA (imaginaria ou real). Tira TUDO e FICA EM PÉ do lado da barraca pelado. UM CAMPER aparece e TE VE. \"ACHOU UM BOM LUGAR?\" Ele SE APROXIMA. \"VOCÊ É O NATURISTA.\" Ele OLHA teu corpo. PASSA a mão na tua barriga. \"BEM VINDO AO CLUBE.\" Ele MANDA: \"DA UMA VOLTA NO ACAMPAMENTO.\" Tu caminha pelado 2 minutos. Ele OLHA e depois ENTRA na barraca dele. Nao goza, seu puto campista.",
|
||||
"🔥 EXPOSTO PEGO FOGO — Vai ate um lugar DESERTO e ACENDE uma FOGUEIRA (ou imaginaria). Tira TUDO. FICA DE CÓCORAS perto do fogo, o corpo iluminado pela chama. Uma pessoa aparece e TE VE na luz do fogo. \"UM RITUAL?\" Ela CHEGA. \"QUE BELA FOGUEIRA.\" Ela OLHA teu corpo iluminado. COLOCA a mão no teu ombro. \"POSSO PARTICIPAR?\" Tu cede. Ela SENTA do outro lado do fogo e OLHA teu corpo nu na luz. 2 minutos de observação. Depois LEVANTA e vai. Nao goza, seu puto do fogo.",
|
||||
"🔥 EXPOSTO PEGO CHUVA — Vai pra lugar DESERTO NA CHUVA. Tira TUDO e FICA EM PÉ na chuva, corpo nu molhado. Uma PESSOA de GUARDA-CHUVA aparece e TE VE. \"QUE COISA LINDA.\" Ela CHEGA e COLOCA o guarda-chuva sobre ti. \"NAO QUERO QUE SE MOLHE DEMAIS.\" Ela OLHA teu corpo molhado. PASSA a mão na tua barriga molhada. \"QUE PELE.\" Depois VAI embora com o guarda-chuva. Tu fica na chuva nu, desprotegido. Nao goza, seu puto molhado.",
|
||||
"🔥 EXPOSTO PEGO SOL — Vai pra lugar DESERTO DE SOL FORTE. Tira TUDO. DEITA de BRUÇOS no chão, tomando sol pelado. Uma PESSOA aparece e TE VE. \"TOMANDO SOL?\" Ela CHEGA. \"PASSA PROTETOR.\" Ela PEGA protetor solar (ou imaginario) e PASSA na tuas costas nuas. Nas NÁDEGAS. No PAU. Cada passada de creme te excita. 2 minutos de massagem com protetor. Ela TERMINA, da UM TAPA na tua bunda e VAI embora. \"NAO QUEIMA, PUTA.\" Nao goza, seu puto bronzeado.",
|
||||
"🔥 EXPOSTO PEGO PISCINA — Vai ate uma PISCINA NATURAL ou LAGOA DESERTA. Tira TUDO e ENTRA na agua pelado. Uma PESSOA aparece na beira e TE VE dentro da agua. \"AGUA BOA?\" Ela TIRA a roupa dela e ENTRA tambem PELADA na agua. NA AGUA, ELA ENCOSTA em ti. COLOCA a mão no teu pau dentro da agua. A agua fria, a mao quente. \"QUE BOM.\" Ela MANDA: \"SAI DA AGUA E MOSTRA COMO CÊ SECA.\" Tu sai da agua e fica em pé pingando na beira. Ela OLHA teu corpo molhado. Depois VOLTA pra agua. Nao goza, sua puta aquatico.",
|
||||
"🔥 EXPOSTO PEGO CAMINHAO CARROCERIA — Vai de BIKE PELADO. Um CAMINHAO com CARROCERIA ABERTA passa e o MOTORISTA TE VE. \"SOBE AÍ, DOIDAO.\" Tu SOBE NA CARROCERIA pelado com a bike. O caminhao ANDA 3 minutos com tu em PÉ na carroceria, NU, o vento batendo. As PESSOAS nos outros carros TE VEEM e ACENAM. 3 minutos de desfile de puta. O caminhao PARA e tu DESCE. \"BOA VIAGEM!\" vai embora. Nao goza, seu puto desfilando.",
|
||||
"🔥 EXPOSTO PEGO ESTRADA POEIRA — Vai pra ESTRADA DE TERRA deserto. Tira TUDO e DEITA NA POEIRA ou BARRO, rolando pelado no chão. O corpo fica SUJO de terra. Uma PESSOA aparece e TE VE sujo de barro. \"QUE ESCULTURA.\" Ela CHEGA. \"TA FAZENDO ARTE.\" Ela OLHA teu corpo sujo. PASSA a mão no barro no teu peito. \"VOU DEIXAR ASSIM.\" Ela MANDA: \"AGORA LEVANTA E MOSTRA A OBRA DE ARTE.\" Tu levanta, sujo de barro, pau duro. Ela da risada e vai. Nao goza, seu puto de barro.",
|
||||
"🔥 EXPOSTO PEGO MONTE — Vai ate um MORRO ou MONTE deserto. SOBE PELADO com a bike na mão (ou a pé). No TOPO, fica EM PÉ olhando a paisagem NU. Uma PESSOA aparece no topo e TE VE. \"QUE VISTA.\" Ela CHEGA e OLHA a paisagem também. DEPOIS OLHA teu corpo nu contra o horizonte. \"VOCÊ É PARTE DA PAISAGEM.\" Ela MANDA: \"FICA AI, IMPRESSIONANTE.\" Tu fica imóvel 2 minutos no topo do monte nu, contra o ceu. Ela VAI embora. Nao goza, seu puto montanhista.",
|
||||
"🔥 EXPOSTO PEGO LAGOA — Vai ate uma LAGOA ou AÇUDE deserto. Tira TUDO. ENTRA na agua, nada um pouco pelado. NA BEIRA, uma PESSOA TA PESCANDO e TE VE nadando. \"PEGA PEIXE?\" Ela OLHA. \"PEGO NADA.\" Ela MANDA: \"SAI DA AGUA E MOSTRA O QUE PEGOU.\" Tu sai da agua, o corpo pingando, o pau duro. Ela OLHA. \"PEGOU UM BONITO.\" Ela da risada e volta a PESCAR. Nao goza, seu puto pescado.",
|
||||
"🔥 EXPOSTO PEGO VENTO — Vai pra um LUGAR ABERTO e VENTOSO. Tira TUDO e FICA EM PÉ com os BRAÇOS ABERTOS, o vento batendo no corpo nu. Uma PESSOA aparece e TE VE de braços abertos, como se quisesse voar. \"VOANDO?\" Ela CHEGA. \"O VENTO TE LEVA.\" Ela ENCOSTA atras de ti e COLOCA as mãos nos teus ombros. FECHA os olhos com tu, sentindo o vento nos dois corpos. 1 minuto juntos. Depois ELA VAI. Nao goza, seu puto do vento.",
|
||||
"🔥 EXPOSTO PEGO AREA — Vai ate uma AREA ou TERRENO BALDIO. Tira TUDO e FICA DE QUATRO no chão, cu pro mundo. Uma PESSOA aparece e TE VE. \"QUE CENA.\" Ela SE AGACHA atras de ti. \"POSSO?\" Ela COLOCA a mão na tua bunda, ABRE o cu com os dedos, e OLHA DENTRO. 30 segundos de inspeção intima. \"LINDO.\" Ela LEVANTA, PASSA a mão na tua cabeca e VAI. Nao goza, seu puto inspecionado.",
|
||||
"🔥 EXPOSTO PEGO MATEIRO — Vai pra uma REGIÃO de MATO FECHADO e trilha. Tira TUDO. CAMINHA PELADO na trilha. Um MATEIRO ou CAÇADOR aparece e TE VE. \"QUE BICHO É ESSE?\" Ele ri. \"BICHO PELADO.\" Ele CHEGA PERTO. \"NAO É BICHO, É GENTE.\" Ele OLHA teu corpo. \"CÊ TA PERDIDO?\" Tu acena que nao. Ele MANDA: \"ENTAO ANDA, QUE EU VOU ATRAZ.\" Tu caminha pelado na trilha com ele te SEGUINDO por 2 minutos, olhando tuas nadegas. Depois ele DESVIA e some. Nao goza, seu puto da mata.",
|
||||
"🔥 EXPOSTO PEGO FINAL 2 — Vai pra um LUGAR ABERTO e VISIVEL, de preferencia onde PASSAM PESSOAS. Tira TUDO. FICA em PÉ de FRENTE pro movimento, BRAÇOS CRUZADOS, PAU DURO, olhando fixo pra quem passa. 3 MINUTOS. Se ALGUÉM TE OLHAR, tu MANTÉM O CONTATO VISUAL SEM DESVIAR, mostrando que não tem vergonha. Quem passa OLHA e VAI. Tu sozinho, nu, firme. Depois veste e volta pra casa. Nao goza, sua puta final.",
|
||||
"🔥 PEGO MAMA PAU — Vai de BIKE PELADO pra lugar desertado. Uma MULHER aparece e TE VE. \"QUE ISSO, UM PELADO NA BIKE?\" Ela CHEGA PERTO e OLHA teu pau DURO. \"ISSO MERECE UM TRATAMENTO.\" Ela SE AGACHA, PEGA teu pau na MÃO e LEVA À BOCA. ELA CHUPA teu pau por 2 MINUTOS — a língua quente, a boca molhada, o olhar fixo em ti. A cabeça vai e vem. Depois levanta, ENXUGA a boca e MANDA: \"AGORA VAI PRA CASA PENSAR EM MIM.\" Tu monta e volta louco. Nao goza, sua puta chupado pela estranha.",
|
||||
"🔥 PEGO CHUPA CU — Vai pra lugar DESERTO, tira TUDO. Fica de QUATRO, cu exposto. Um HOMEM aparece e TE VE. \"QUE CUZINHO GOSTOSO.\" Ele CHEGA e SE AGACHA atras. Ele ABRE teu cu com as mãos e COLOCA A BOCA. A LINGUA entra no teu cu. 2 MINUTOS de lambedera de cu. A língua quente Macia, abrindo teu cu. Tu geme baixo. Depois LEVANTA e MANDA: \"AGORA VAI PRA CASA COM O CU LAMBIDO.\" Tu veste e vai. Nao goza, seu puto lambido.",
|
||||
"🔥 PEGO BEBE GOZO — Vai de BIKE PELADO. Um HOMEM aparece e TE ABORDA. \"QUER VER UM NEGÓCIO?\" Ele ABRE a CALÇA e COMEÇA a se MASTURBAR na tua frente. O pau dele duro. Ele MASTURBA RÁPIDO até GOZAR NA TUA CARA. A porra quente espirra nos teus olhos, na boca. Ele MANDA: \"ABRE A BOCA.\" Tu abre. Ele COLOCA o pau na tua boca e ESCORRE o resto do gozo na tua língua. \"ENGole TUDO.\" Tu engole o gozo dele. Ele ARRUMA a calça e VAI embora. Nao goza, seu puto bebendo porra.",
|
||||
"🔥 PEGO COME CU — Vai BIQUE PELADO pra lugar deserto. Uma MULHER aparece e TE VE de quatro. \"QUE CENA GOSTOSA.\" Ela ABRE a CALÇA e ELA TEM UM PAU DURO (ou cintaralho). Ela COLOCA CAMISINHA e se APROXIMA de ti de quatro. \"ABRE ESSE CU PRA MAMÃE.\" Tu abre o cu com as mãos. Ela ENFIA O PAU NO TEU CU — devagar, sentindo cada milímetro. Ela TE COME por 3 MINUTOS — vai e vem, mão na tua cintura, gemendo. Depois GOZA DENTRO (com camisinha) e tira. \"GUARDA ESSA LEITADA.\" Vai embora. Nao goza, seu puto comido pela mulher.",
|
||||
"🔥 PEGO COME BUCETA — Vai de BIKE PELADO. Uma MULHER aparece e TE VE pelado. \"QUE HOMEM LINDO.\" Ela TIRA a CALÇA dela e se AGACHA. ELA TA PELADA EMBAIXO. \"VEM COMER ESSA BUCETA.\" Ela te PUXA e tu SOBE nela. Tu enfia O PAU na BUCETA dela — quente, molhada. 5 MINUTOS comendo a estranha. Ela geme, tu metendo. No final ela GOZA e te EMPURRA. \"BOA, PUTA. AGORA VAI.\" Tu veste e vai. Nao goza, seu puto comedor de buceta.",
|
||||
"🔥 PEGO SUGANDO — Vai pra lugar DESERTO, tira TUDO. Uma MULHER aparece e TE VE. \"QUE PAU GOSTOSO.\" Ela PEGA teu pau e COLOCA na BOCA DELE — ela CHUPA teu pau com vontade, a cabeça subindo e descendo, babando tudo. 3 MINUTOS de BOQUETE. Ela olha pra ti enquanto chupa. Depois TIRA e MANDA: \"AGORA ME LAMBE O CU.\" Ela vira e se curva. Tu LAMBE o cu dela com a língua inteira, lambendo tudo. 2 minutos de cu lambido. Ela geme, LEVANTA e VAI. Nao goza, seu puto lingual.",
|
||||
"🔥 PEGO CASA ESTILO — Vai de BIKE PELADO. Um CASAL (HOMEM E MULHER) TE VE. A MULHER MANDA: \"QUE GATO PELADO.\" Ela OLHA pro homem: \"VAI COMER ELE.\" O HOMEM VEM, COLOCA a mão na tua nuca. \"ABRE A BOCA.\" Ele COLOCA O PAU na tua boca. Tu CHUPA o pau dele. 2 MINUTOS de boquete. Depois a MULHER MANDA: \"AGORA ABRE O CU PRO MEU MARIDO TE COMER.\" Tu abre o cu com as mãos e o HOMEM TE COME — vai e vem, gemendo, 3 minutos. GOZA DENTRO (com camisinha). A MULHER OLHA e MANDA: \"AGORA LAMBE O PAU DELE DEPOIS DE GOZAR.\" Tu lambe o pau sujo do homem. Depois o casal vai embora. Nao goza, seu puto do casal.",
|
||||
"🔥 PEGO CASA MULHER MANDA — Vai de BIKE PELADO. Um CASAL aparece. A MULHER MANDA: \"MEU MARIDO VAI TE COMER E VOCE VAI OBEDECER.\" Ela SEGURA o braço enquanto o HOMEM ABRE a calça. \"ABRE A BOCA E CHUPA O PAU DELE.\" Tu CHUPA o pau do homem 2 minutos. A mulher OLHA. Depois MANDA: \"AGORA DEITA DE QUATRO QUE ELE VAI TE COMER.\" Tu deita de quatro. O HOMEM enfia o pau no teu cu. 3 minutos. A mulher: \"GOZA DENTRO DELE.\" O HOMEM GOZA. Depois a mulher MANDA: \"LAMBE O CU DO MEU MARIDO DEPOIS DELE GOZAR.\" Tu lambe. O CASAL vai embora. Nao goza, seu puto comandado pela mulher.",
|
||||
"🔥 PEGO BEBE GOZO 2 — Vai pra lugar DESERTO, tira TUDO. Um HOMEM aparece e TE VE. \"QUER PROVAR?\" Ele MASTURBA na tua frente. \"ABRE A BOCA.\" Ele GOZA NA TUA BOCA. A porra quente enche tua boca. \"ENGole.\" Tu engole. \"MAIS?\" Ele MASTURBA de novo até gozar de novo. A segunda gozada. MAIS GROSSA. \"ENGole TUDO.\" Tu engole de novo. Ele ARRUMA a calça e VAI. Nao goza, seu puto de duas gozadas.",
|
||||
"🔥 PEGO CHUPA PAU CASAL — Vai de BIKE PELADO. Um CASAL DE HOMENS TE VE. UM DELES MANDA: \"QUE PELADO GOSTOSO, VEM CA.\" Tu vai. Ele COLOCA O PAU na tua boca. \"CHUPA.\" Tu chupa 2 minutos. O OUTRO HOMEM MANDA: \"AGORA CHUPA O MEU.\" Tu troca de pau na boca — chupa o outro 2 minutos. Os dois OLHAM. UM MANDA: \"VIRA E MOSTRA O CU.\" Tu vira. O OUTRO enfia o pau no teu cu. O PRIMEIRO COLOCA O PAU na tua boca ao mesmo tempo. Tu de quatro, com pau na boca e no cu, 3 MINUTOS. OS DOIS GOZAM em ti. Tu limpa e veste. Nao goza, seu puto de dois.",
|
||||
"🔥 PEGO LINGUA CU CASAL — Vai pra lugar DESERTO, tira TUDO. Um CASAL aparece. A MULHER MANDA: \"LAMBE O CU DO MEU MARIDO.\" Ela segura enquanto tu LAMBE o cu do homem. 2 minutos de língua no cu do cara. Depois o HOMEM MANDA: \"AGORA LAMBE O CU DA MINHA MULHER.\" Tu lambe o cu da mulher. A língua no cu feminino. Mais 2 minutos. O CASAL se OLHA e MANDA: \"AGORA DEITA DE QUATRO PRO MEU MARIDO TE COMER ENQUANTO EU VEJO.\" Tu deita, o homem te come, a mulher assiste. Nao goza, seu puto lingual de casal.",
|
||||
"🔥 PEGO ENFIA DEDO — Vai de BIKE PELADO. Uma MULHER aparece e TE VE. \"QUE PAU DURO.\" Ela COLOCA a mão no teu pau e alisa. \"MAS QUERO OUTRA COISA.\" Ela TE VIRA de costas. \"ABRE ESSE CU.\" Tu abre. Ela ENFIA DOIS DEDOS no teu cu. Ela MEXE os dedos dentro de ti — um vai e vem lento. 2 MINUTOS de dedos no cu. Ela TIRA e LAMBE os dedos. \"BOM.\" Vai embora. Nao goza, seu puto dedado pela estranha.",
|
||||
"🔥 PEGO CU CAVALGADA — Vai pra lugar DESERTO, tira TUDO. Uma MULHER de CAVALO aparece e TE VE. Ela DESCE do cavalo. \"QUE VISTO.\" Ela COLOCA a mão na tua bunda. \"CÊ MERECE SER COMIDO.\" Ela TE DEITA de BRUÇOS no chão, SOBE em cima de ti, e COLOCA O PAU DELA (cintaralho) no teu cu. ELA MONTADA em ti, cavalgando teu cu. 3 MINUTOS de cavalgada. ELA GOZA em cima de ti e cai pro lado. Monta no cavalo e VAI. Nao goza, seu puto montado.",
|
||||
"🔥 PEGO BOCA ABERTA — Vai de BIKE PELADO. Um HOMEM aparece e TE PARA. \"ABRE A BOCA.\" Tu abre. Ele COLOCA O PAU na tua boca e ENFIA até o FUNDO. A garganta dele entrando. Tu quase engasga. Ele SEGURA tua cabeça e METe na tua boca — 2 MINUTOS de gag. Depois GOZA na tua boca e MANDA: \"ENGole.\" Tu engole. Ele SAI. Nao goza, seu puto engasgado.",
|
||||
"🔥 PEGO CADEIA — Vai pra lugar DESERTO, tira TUDO. Um HOMEM aparece e TE DOMINA. \"MÃOS NAS COSTAS.\" Ele te AMARRA as mãos. \"VAI CHUPAR MEU PAU.\" Ele COLOCA O PAU na tua boca enquanto tu de mãos presas. 2 MINUTOS de boquete amarrado. Depois TE VIRA. \"ABRE O CU.\" Tu abre e ele TE COME — vai e vem, 3 minutos. GOZA DENTRO. TE SOLTA. \"PODE IR.\" Tu veste e sai. Nao goza, seu puto amarrado e comido.",
|
||||
"🔥 PEGO DOIS HOMENS — Vai de BIKE PELADO. DOIS HOMENS aparecem e TE RODEIAM. UM MANDA: \"CHUPA O PAU DO MEU AMIGO.\" Tu chupa o pau do primeiro. O OUTRO MANDA: \"ABRE O CU PRO MEU.\" Tu deita de quatro. O segundo enfia no teu cu. Tu com pau na boca e no cu ao mesmo tempo. 4 MINUTOS de dupla penetração. O PRIMEIRO GOZA na tua boca. O SEGUNDO GOZA no teu cu. Tu engole e limpa. \"BOA, PUTA.\" VAO EMBORA. Nao goza, seu puto de dois caras.",
|
||||
"🔥 PEGO MULHER COMENDO — Vai pra lugar DESERTO, tira TUDO. Uma MULHER aparece e TE VE. \"GOSTEI.\" Ela TIRA a calça, SENTA no chão e ABRE as pernas. \"VEM COMER MINHA BUCETA.\" Tu SOBE nela e enfia o PAU na buceta dela. 5 MINUTOS de metelança. Ela geme, aperta, morde teu ombro. \"GOZA DENTRO DE MIM.\" Tu GOZA dentro da buceta dela (com ou sem camisinha conforme combinado). Ela se ABANA e MANDA: \"BOA. AGORA VAI.\" Tu veste e vai. Nao goza de novo — já gozou. Pode gozar. Seu puto comedor.",
|
||||
"🔥 PEGO BUCETA LAMBE — Vai de BIKE PELADO. Uma MULHER aparece e TE VE. \"VEM AQUI, GOSTOSO.\" Ela se SENTA numa pedra, ABRE as pernas e COLOCA teu ROSTO na BUCETA dela. \"LAMBE.\" Tu LAMBE a buceta dela com a língua inteira — 3 MINUTOS de linguada. O gosto da buceta na tua boca. Ela geme, aperta tua cabeça contra ela. Depois GOZA na tua boca — o orgasmo dela escorrendo. \"ENGole.\" Tu engole. Ela LEVANTA e vai. Nao goza, seu puto lambedor de buceta.",
|
||||
"🔥 PEGO PAU NA BOCA E CU — Vai de BIKE PELADO. Uma MULHER e um HOMEM aparecem. A MULHER MANDA: \"CHUPA O PAU DELE.\" Tu chupa o pau do homem. O HOMEM MANDA: \"ABRE O CU PRA ELA.\" Tu abre o cu. A MULHER ENFIA O PAU (cintaralho) no teu cu enquanto tu chupa o pau do homem. 4 MINUTOS de dupla penetração: boca nele, cu nela. A MULHER GOZA no teu cu, o HOMEM GOZA na tua boca. Tu engole e limpa. O CASAL vai embora. Nao goza, seu puto dos dois.",
|
||||
"🔥 PEGO BEBE LEITE PUTO — Vai de BIKE PELADO. Um HOMEM aparece e TE ABORDA. \"VOCÊ VAI BEBER MEU LEITE.\" Ele MASTURBA na tua frente, olho no olho. Vai devagar, depois rápido. GOZA na tua mão (ou na sua) e COLOCA a porra na tua BOCA. \"CHUPA QUE NEM MAMADEIRA.\" Tu chupa os dedos dele com o gozo. \"BOM?\" Tu acena. \"MAIS?\" Ele se masturba de novo ATÉ GOZAR de novo. COLOCA na tua boca de novo. Tu engole a segunda porra. \"VAI PRA CASA AGORA.\" Tu monta e vai. Nao goza, seu puto mamadeira.",
|
||||
"🔥 PEGO COMIDA POR CASAL — Vai pra lugar semideserto, tira TUDO. Um CASAL TE VE. A MULHER: \"QUE HOMEM LINDO.\" O HOMEM: \"VAI SERVIR A GENTE.\" A MULHER MANDA: \"DEITA DE QUATRO. MEU MARIDO VAI TE COMER ENQUANTO EU VEJO.\" Tu deita de quatro. O HOMEM TE COME — vai e vem, 3 minutos. Depois a MULHER MANDA: \"AGORA VEM CHUPAR MEU CU ENQUANTO ELE TE COME.\" Tu lambe o cu da mulher enquanto o homem te come. Ciclo completo: cu lambido, cu comido. O homem GOZA no teu cu. A mulher GOZA na tua boca. Nao goza, seu puto servindo o casal.",
|
||||
"🔥 PEGO SEXO TOTAL — Vai de BIKE PELADO. Uma MULHER aparece e TE LEVA pra um local escondido. \"VOU TE USAR INTEIRO.\" Ela TE DEITA, COLOCA teu pau na BOCA dela e CHUPA 2 MINUTOS. Depois SOBE em ti e COLOCA teu pau na BUCETA dela. 3 MINUTOS cavalgando. Depois VIRA e COLOCA o CU dela na tua cara. \"LAMBE MEU CU ENQUANTO EU MONTO EM TI.\" Tu lambe o cu dela enquanto ela cavalga teu pau. 2 MINUTOS. Depois DESCE, MANDA: \"ABRE A BOCA.\" e GOZA na tua boca. \"BOA, PUTA.\" Tu tudo usado. Nao goza, seu puto usado.",
|
||||
"🔥 PEGO DOMINADO POR CASAL — Vai pra lugar DESERTO, tira TUDO. Um CASAL TE ENCONTRA. A MULHER: \"ACHAMOS UM BRINQUEDO.\" O HOMEM: \"VAI OBEDECER.\" A MULHER MANDA: \"CHUPA O PAU DELE.\" Tu chupa o homem. Depois o HOMEM MANDA: \"LAMBE O CU DELA.\" Tu lambe o cu da mulher. DEPOIS A MULHER: \"ABRE O CU PRO MEU MARIDO TE COMER ENQUANTO EU CHUPO TEU PAU.\" Tu deita de quatro, o homem te come, e a mulher chupa teu pau ao mesmo tempo. 5 MINUTOS de sexo total. OS DOIS GOZAM em ti. Nao goza, seu puto brinquedo do casal.",
|
||||
"🔥 PEGO SIRIRICA — Vai de BIKE PELADO. Uma MULHER aparece e TE VE. \"VOU TE DAR UM PRESENTE.\" Ela TIRA a calcinha, ABRE as pernas e SE MASTURBA na tua frente. Tu vendo a buceta dela, os dedos entrando. ELA GOZA na mão dela e COLOCA a mão molhada na tua boca. \"CHUPA MEU GOZO.\" Tu chupa os dedos molhados de buceta. O gosto dela. \"BOM?\" Ela monta na bike dela e vai. Nao goza, seu puto lambedor de gozo feminino.",
|
||||
"🔥 PEGO TROCA DE CASAL — Vai pra lugar DESERTO, tira TUDO. DUAS MULHERES e UM HOMEM aparecem. UMA MULHER MANDA: \"VOCÊ VAI SERVIR A GENTE.\" Ela COLOCA o pau do homem na tua boca. A OUTRA MULHER MANDA: \"ABRE O CU PRO MEU MARIDO.\" O homem te come enquanto tu chupa o pau imaginario (ou so fazendo). Depois UMA MULHER SENTA na tua cara: \"LAMBE MINHA BUCETA ENQUANTO ELE TE COME.\" Tu lambe a buceta enquanto o homem te come. A OUTRA COLOCA o peito na tua mão. Depois TUDO GOZA. Nao goza, seu puto servindo o grupo.",
|
||||
"🔥 PEGO AMIGA — Vai de BIKE PELADO. UMA AMIGA aparece e TE VE. \"CARALHO, É VOCÊ?\" Ela da risada, mas OLHA teu corpo e PARECE INTERESSADA. Ela CHEGA. \"DEIXA EU VER.\" Ela COLOCA a mão no teu pau. \"NUNCA VI O TEU.\" Ela ALISA a cabecinha. Depois SE AGACHA e COLOCA NA BOCA. Ela CHUPA teu pau 2 MINUTOS — amiga chupando amigo. Depois LEVANTA. \"NINGUEM PRECISA SABER.\" Ela vai embora. Tu monta na bike e vai. Nao goza, seu puto chupado pela amiga.",
|
||||
"🔥 PEGO SEXO AMIGA — Vai de BIKE PELADO e ENCONTRA UMA AMIGA no lugar deserto. Ela TE VE e PERGUNTA: \"TA PELADO POR QUE?\" Tu explica. Ela OLHA e PERGUNTA: \"QUER TRANZAR?\" Tu acena. Ela TIRA a calça e SENTA na grama. \"VEM.\" Tu SOBE nela. Enfia na BUCETA. 5 minutos de sexo com a amiga. Ela geme, tu mete. Depois ELA GOZA e PERGUNTA: \"QUER GOZAR ONDE?\" Tu GOZA na barriga dela. Ela LIMPA e VAI embora rindo. Amiga com direito. Pode gozar, seu puto.",
|
||||
"🔥 PEGO AMIGO — Vai de BIKE PELADO. Um AMIGO aparece e TE VE. \"CARALHO, TU TA PELADO.\" Ele OLHA e PERCEBE teu pau duro. \"ISSO É CONVITE?\" Tu fala que sim. Ele ABRE a CALÇA e COLOCA O PAU na tua boca. \"CHUPA.\" Tu chupa o pau do amigo 2 MINUTOS. Depois VIRA: \"ABRE O CU.\" Tu abre e ele TE COME 3 MINUTOS. GOZA no teu cu (com camisinha) e MANDA: \"ISSO FICA ENTRE NÓS.\" Vai embora. Nao goza, seu puto comido pelo amigo.",
|
||||
"🔥 PEGO CUNHADA — Vai de BIKE PELADO. A CUNHADA (ou amiga da familia) TE VE. \"NOSSAAAA.\" Ela OLHA e PERGUNTA: \"É ASSIM QUE ANDA?\" Ela CHEGA, PASSA a mão no teu pau e CHUPA teu pau 2 MINUTOS. Depois MANDA: \"ABRE O CU PRO DEDO.\" Ela enfia o dedo no teu cu. 2 minutos de dedada. Depois MANDA: \"LAMBE MEU CU.\" Ela vira e tu lambe o cu dela. Depois ELA VAI. \"SEU SEGREDO.\" Nao goza, seu puto da cunhada.",
|
||||
"🔥 PEGO BEBE GOZO GRUPO — Vai de BIKE PELADO. TRÊS HOMENS aparecem e TE RODEIAM. UM MANDA: \"ABRE A BOCA.\" Os três MASTURBAM na tua frente, em circulo. O PRIMEIRO GOZA na tua boca. Tu ENGole. O SEGUNDO GOZA na tua boca. Tu ENGole. O TERCEIRO GOZA na tua boca. Tu ENGole TUDO. \"BOA GAROTA.\" ELES VAO EMBORA. Tu monta e vai. Nao goza, seu puto de 3 gozadas.",
|
||||
"🔥 PEGO GRUPO SEXO — Vai de BIKE PELADO. DUAS MULHERES E UM HOMEM aparecem e TE VEEM. A MULHER MANDA: \"VOCE VAI SERVIR A GENTE.\" O HOMEM COLOCA o pau na tua boca. UMA MULHER ENFIA o dedo no teu cu. A OUTRA COLOCA a buceta na tua mão pra tu masturbar. 5 MINUTOS de sexo grupal. O HOMEM GOZA na tua boca. A MULHER GOZA na tua mão. A OUTRA te beija. \"VALEU, PUTA.\" Vao embora. Nao goza, seu puto servindo o grupo.",
|
||||
"🔥 PEGO CADEIA 2 — Vai pra lugar deserto, tira TUDO. DOIS HOMENS aparecem e te AMARRAM numa arvore (frouxo). \"VOCE VAI SERVIR.\" Um COLOCA O PAU na tua boca. O OUTRO enfia o pau no teu cu. Tu amarrado, recebendo dos dois lados. 5 MINUTOS de dupla. OS DOIS GOZAM em ti. Depois TE SOLTAM e vao. Nao goza, seu puto amarrado e servido.",
|
||||
"🔥 PEGO MAMA PAU 2 — Vai de BIKE PELADO. Uma MULHER VELHA (ou SENHORA) aparece e TE VE. \"MENINO, QUE ISSO?\" Ela OLHA teu pau. \"QUE COISA LINDA.\" Ela SE AGACHA (com dificuldade) e COLOCA NA BOCA. Ela CHUPA teu pau DEVAGAR, com carinho de avó puta. 2 MINUTOS de boquete de senhora. Depois LEVANTA e MANDA: \"GUARDA ESSA JOIA.\" Vai embora. Nao goza, seu puto chupado pela idosa.",
|
||||
"🔥 PEGO MAMA BUCETA — Vai de BIKE PELADO. Um HOMEM aparece e TE VE. \"VEM AQUI.\" Ele COLOCA A BOCA no teu pau e CHUPA. 2 MINUTOS de boquete masculino. Depois LEVANTA, VIRA e MANDA: \"LAMBE MEU CU.\" Tu lambe o cu do homem 2 minutos. Depois ELE VIRA e MANDA: \"ABRE A BOCA.\" Ele GOZA na tua boca. Tu ENGole. Nao goza, seu puto de lingua e boca.",
|
||||
"🔥 PEGO AMIGA 2 — Vai de BIKE PELADO. Uma AMIGA te ENCONTRA no lugar deserto. \"QUE ISSO?\" Ela OLHA teu pau e PERGUNTA: \"POSSO?\" Ela COLOCA na boca e CHUPA 2 MINUTOS. Depois MANDA: \"ME COME.\" Tu Sobe nela e enfia na buceta. 5 MINUTOS transando com a amiga. Ela GOZA e fala: \"GOZA DENTRO DE MIM (com camisinha).\" Tu GOZA. Ela se LEVANTA. \"FOI BOM.\" Vai embora. Pode gozar, seu puto.",
|
||||
"🔥 PEGO GRUPO MISTO — Vai de BIKE PELADO. DUAS MULHERES E DOIS HOMENS aparecem e TE RODEIAM. \"VAMOS BRINCAR.\" UMA MULHER CHUPA teu pau. O OUTRO HOMEM COLOCA o pau na tua boca. A OUTRA MULHER ENFIA dedos no teu cu. O SEGUNDO HOMEM se masturba te olhando. 5 MINUTOS de sexo grupal intenso. TODOS GOZAM em ti. Tu sujo de porra, gozo, baba. Depois VAO embora. Nao goza, seu puto do grupo todo.",
|
||||
"🔥 PEGO VELHO — Vai pra lugar DESERTO, tira TUDO. Um HOMEM IDOSO aparece e TE VE. \"QUE JOVEM BONITO.\" Ele SE APROXIMA. \"ME AJUDA AÍ.\" Ele ABRE a calça com dificuldade. O pau dele MEIO DURO. \"COLOCA NA BOCA.\" Tu COLOCA na boca e CHUPA devagar. 2 MINUTOS de boquete no idoso. Ele GOZA na tua boca. \"OBRIGADO, FILHO.\" Ele vai embora. Nao goza, seu puto do idoso.",
|
||||
"🔥 PEGO CADEIA MULHER — Vai de BIKE PELADO. Uma MULHER aparece e TE DOMINA. \"MÃOS NAS COSTAS.\" Ela te AMARRA com uma corda. \"ABRE A BOCA.\" Ela COLOCA O CINTARALHO (ou pau imaginario) na tua boca. \"CHUPA.\" Tu chupa 2 minutos. Depois TE VIRA: \"ABRE O CU.\" Ela ENFIA o cintaralho no teu cu. 3 MINUTOS de mulher te comendo. Ela GOZA, TE SOLTA e VAI. Nao goza, seu puto amarrado pela mulher.",
|
||||
"🔥 PEGO CASAL MULHER MANDA 2 — Vai de BIKE PELADO. Um CASAL TE VE. A MULHER MANDA PRO HOMEM: \"VAI CHUPAR O PAU DELE.\" O HOMEM VEM e CHUPA teu pau 2 MINUTOS. A MULHER OLHA. Depois MANDA PRA TI: \"AGORA LAMBE O CU DO MEU MARIDO.\" Tu lambe o cu do homem enquanto a mulher assiste. 2 MINUTOS. Depois A MULHER MANDA: \"AGORA VAI COMER MINHA BUCETA ENQUANTO ELE TE COME.\" Tu deita de quatro, o homem te come, e tu mete na buceta da mulher. 3 MINUTOS de sexo triangular. Todos GOZAM. Nao goza, seu puto da triangulação.",
|
||||
"🔥 PEGO BEBE LEITE MAMA — Vai de BIKE PELADO. Uma MULHER GRÁVIDA ou AMAMENTANDO aparece e TE VE. \"QUE BEBÊ GULOSO.\" Ela APERTA o próprio peito e ESPIRRA LEITE no teu rosto. \"ABRE A BOCA.\" Ela COLOCA o peito na tua boca e tu MAMA leite materno. 2 MINUTOS mamando peito. Depois ela LIBERA e MANDA: \"AGORA VAI PRA CASA, BEBÊ.\" Vai embora. Nao goza, seu puto mamando.",
|
||||
"🔥 PEGO LAMBE TUDO — Vai de BIKE PELADO. Uma MULHER aparece e TE VE. ELA TIRA TUDO e FICA PELADA na tua frente. \"LAMBE MEU CORPO INTEIRO.\" Tu LAMBE ela do pescoço até a buceta. 3 MINUTOS lambendo cada centímetro. Depois ELA VIRA e MANDA: \"LAMBE MEU CU.\" Tu lambe o cu dela com a lingua. Ela GOZA na tua boca. Vai embora. Nao goza, seu puto lambedor.",
|
||||
"🔥 PEGO CHUPA PAU MULHER — Vai de BIKE PELADO. Uma MULHER aparece e TE ABORDA. ELA ABRE a CALÇA e ELA TEM UM PAU de verdade (ou cintaralho). \"ABRE A BOCA.\" Ela COLOCA na tua boca e tu CHUPA o pau dela. 3 MINUTOS de boquete numa mulher de pau. Ela GOZA na tua boca. Tu ENGole. \"BOA, PUTA.\" Vai embora. Nao goza, seu puto chupando pau de mulher.",
|
||||
"🔥 PEGO CASAL AMIGOS — Vai de BIKE PELADO e ENCONTRA UM CASAL AMIGO. ELES TE VEEM e, em vez de surpresa, TOPAM A BRINCADEIRA. A MULHER: \"VAMOS BRINCAR?\" O HOMEM: \"CLARO.\" Eles TIRAM a roupa. A MULHER CHUPA teu pau. O HOMEM COLOCA o pau na tua boca. 4 MINUTOS de troca. Depois A MULHER MANDA: \"AGORA VEM COMER MINHA BUCETA ENQUANTO ELE TE COME.\" Tu na buceta dela, ele no teu cu. 3 MINUTOS. GOZAM todos. Amigos putos. Pode gozar, seu puto.",
|
||||
"🔥 PEGO MULHER CHUPA CU — Vai de BIKE PELADO. Uma MULHER aparece e TE VE. ELA SE AGACHA e PERGUNTA: \"POSSO?\" Ela ABRE tua bunda e COLOCA A BOCA no teu cu. A língua entrando, lambendo tudo. 3 MINUTOS de cu lambido por mulher. Tu geme. Ela PARA e MANDA: \"AGORA LAMBE O MEU.\" Ela vira e tu lambe o cu dela. 2 MINUTOS. Depois ELA VAI. Nao goza, seu puto de cu lambido e lambedor.",
|
||||
"🔥 PEGO TUDO EM UM — Vai pra lugar semideserto, tira TUDO. DOIS HOMENS E DUAS MULHERES aparecem. UMA MULHER MANDA: \"VOCE VAI SERVIR A GENTE INTEIRO.\" O PRIMEIRO HOMEM COLOCA o pau na tua boca. A PRIMEIRA MULHER ENFIA a língua no teu cu. O SEGUNDO HOMEM COLOCA o pau no teu cu (depois que ela lambe). A SEGUNDA MULHER COLOCA a buceta na tua mão. Tu servindo 4 pessoas ao mesmo tempo. 5 MINUTOS de sexo em grupo. TODOS GOZAM. Tu sujo de leite e baba. Depois VAO embora. Nao goza, seu puto de 4.",
|
||||
"🔥 PEGO FINAL SEXO — Depois de uma pedalada pelada, PARA num lugar e tira TUDO. Um HOMEM ou MULHER aparece e TE ABORDA. \"QUER TRANZAR?\" Tu aceita. ELA/ELE TE LEVA pra um local escondido e TRANZA com tu — buceta, cu, boca, TUDO. 10 MINUTOS de sexo completo. GOZA junto com a pessoa. Depois se DESPEDE e cada um vai pro seu lado. Pode gozar, seu puto final.",
|
||||
"🔥 PEGO GRUPO DEDO CU — Vai de BIKE PELADO pra lugar deserto. Três PESSOAS aparecem (dois homens, uma mulher). UM HOMEM MANDA: \"DEITA DE QUATRO.\" Tu obedece. A MULHER ENFIA DOIS DEDOS no teu cu — dedos entrando e saindo devagar, abrindo. 2 minutos. O OUTRO HOMEM COLOCA O PAU na tua boca enquanto a mulher te deda. Tu chupando e sendo dedado ao mesmo tempo. O PRIMEIRO HOMEM COLOCA O PAU no teu cu junto com os dedos da mulher. 3 minutos de dupla penetração com dedos. Todos GOZAM em ti e vão. Nao goza, seu puto dedado e comido.",
|
||||
"🔥 PEGO CASAL DEDO — Vai de BIKE PELADO. Um CASAL aparece e TE VE de quatro numa pedra (ou no chao). A MULHER MANDA PRO HOMEM: \"VAI DEDAR ELE ENQUANTO EU VEJO.\" O HOMEM SE AGACHA e ENFIA TRÊS DEDOS no teu cu — abre bem, estica. 2 MINUTOS de dedos no cu enquanto a mulher assiste. Depois ELA MANDA PRO HOMEM: \"AGORA ENFIA O PAU ENQUANTO EU CHUPO O DELE.\" O homem enfia o pau no teu cu e a mulher CHUPA teu pau. 3 MINUTOS de sexo duplo. O HOMEM GOZA no teu cu, a MULHER GOZA na tua boca (pede pra tu chupar ela depois). Nao goza, seu puto dedado por casal.",
|
||||
"🔥 PEGO DEITADO DEDO — Vai de BIKE PELADO. Uma MULHER aparece e TE VE. \"DEITA DE COSTAS E ABRE AS PERNAS.\" Tu deita e abre. Ela CHEGA e ENFIA O DEDO no teu cu, devagar, olhando nos teus olhos. O dedo entra todo. 2 MINUTOS de dedo parado sentindo o cu apertar. Depois ela MEXE o dedo dentro — vai e vem lento. \"RELXA.\" Mais 2 minutos de dedo se movendo. Depois TIRA, LAMBE o dedo e MANDA: \"AGORA VAI PRA CASA COM ESSA SENSAÇÃO.\" Nao goza, seu puto dedado deitado.",
|
||||
"🔥 PEGO QUATRO DEDOS GRUPO — Vai de BIKE PELADO. DUAS MULHERES aparecem. UMA MANDA: \"DE QUATRO.\" Tu deita de quatro. AS DUAS SE AGACHAM atras de ti. UMA ENFIA DOIS DEDOS no teu cu. A OUTRA ENFIA DOIS DEDOS no teu cu — QUATRO DEDOS ao MESMO TEMPO. O cu esticado no limite. 2 MINUTOS de quatro dedos dentro. Depois ELAS ABREM os dedos em tesoura — estica mais. TIRAM devagar. \"QUE CU GOSTOSO.\" VAO embora. Nao goza, seu puto de quatro dedos.",
|
||||
"🔥 PEGO MAMA CU CASAL — Vai de BIKE PELADO. Um CASAL TE VE. A MULHER MANDA: \"DEITA DE QUATRO PRO MEU MARIDO LAMBER TEU CU.\" Tu deita. O HOMEM LAMBE teu cu por 3 MINUTOS — a lingua abrindo, entrando. Depois a MULHER MANDA: \"AGORA LAMBE O CU DELE.\" Tu lambe o cu do homem. Depois ELA: \"AGORA VEM CHUPAR MINHA BUCETA ENQUANTO ELE TE COME.\" Tu chupa a buceta da mulher enquanto o homem te come. Nao goza, seu puto do casal lambedor.",
|
||||
"🔥 PEGO CU ABERTO GRUPO — Vai de BIKE PELADO. TRES PESSOAS aparecem (dois homens, uma mulher). ELES MANDAM: \"DE QUATRO E ABRE O CU.\" Tu deita de quatro e abre o cu com as mãos. A MULHER ENFIA o dedo primeiro. Depois UM HOMEM ENFIA O PAU. Depois o OUTRO HOMEM COLOCA na tua boca. Os tres te usando ao mesmo tempo. 5 MINUTOS. TODOS GOZAM. Vao embora. Nao goza, seu puto arrombado pelo grupo.",
|
||||
"🔥 PEGO MULHER DOMINA — Vai de BIKE PELADO. Uma MULHER aparece e TE DOMINA. \"MÃOS NA CABEÇA. AJOELHA.\" Tu ajoelha. Ela ABRE a CALÇA e tem um PAU (cintaralho ou strap). \"ABRE A BOCA.\" Ela COLOCA na tua boca. 2 MINUTOS de boquete. Depois \"DE QUATRO.\" Ela ENFIA O PAU no teu cu. 3 MINUTOS de mulher te comendo. GOZA em ti. \"BOA, PUTA.\" Vai embora. Nao goza, seu puto dominado pela mulher.",
|
||||
"🔥 PEGO MAMA PAU CASAL 2 — Vai de BIKE PELADO. Um CASAL TE VE e PARA. A MULHER MANDA PRO HOMEM: \"VAI CHUPAR O PAU DELE ENQUANTO EU VEJO.\" O HOMEM CHUPA teu pau 2 MINUTOS enquanto a mulher assiste. Depois ELA MANDA: \"AGORA VEM CHUPAR MEU CU ENQUANTO ELE TE COME.\" Tu lambe o cu da mulher enquanto o homem te come. 3 MINUTOS de sexo triangular. Os dois gozam. Nao goza, seu puto do casal.",
|
||||
"🔥 PEGO BEBE PORRA 3 — Vai de BIKE PELADO. TRÊS HOMENS aparecem e TE RODEIAM. \"ABRE A BOCA.\" Eles se masturbam em circulo ao teu redor. O PRIMEIRO GOZA na tua boca. Tu ENGole. O SEGUNDO GOZA na tua boca. Tu ENGole. O TERCEIRO GOZA no teu ROSTO. Tu limpa com a boca. \"BOA GAROTA DE BUCETÃO.\" Vao embora. Nao goza, seu puto de 3 porradas.",
|
||||
"🔥 PEGO AMIGA DEDO — Vai de BIKE PELADO. UMA AMIGA aparece e TE VE. \"RELXA, SOU EU.\" Ela CHEGA e PERGUNTA: \"POSSO DEDAR?\" Tu acena. Ela ENFIA DOIS DEDOS no teu cu — na posição de quatro. Os dedos entram devagar, sentindo cada anel muscular. 3 MINUTOS de amiga dedando teu cu. Os dedos entram e saem, massageiam a prostata. Ela TIRA e LAMBE. \"VALEU, AMIGO.\" Vai embora. Nao goza, seu puto dedado pela amiga.",
|
||||
"🔥 PEGO IRMAO — Vai de BIKE PELADO. Um IRMÃO ou PRIMO aparece e TE VE. \"CARALHO, TU TA PELADO?\" Ele OLHA e PERCEBE a situação. \"VOU APROVEITAR.\" Ele ABRE a CALÇA e MANDA: \"ABRE A BOCA.\" Tu CHUPA o pau do irmao/ primo. 3 MINUTOS de boquete. Depois \"DE QUATRO.\" Ele TE COME — vai e vem, 3 MINUTOS. GOZA fora. \"ISSO FICA ENTRE NÓS.\" Vai embora. Nao goza, seu puto da família.",
|
||||
"🔥 PEGO VIZINHO SEXO — Vai de BIKE PELADO. O VIZINHO aparece e TE VE. \"DE NOVO, PUTA?\" Ele te EMPURRA pro muro. \"ABRE A BOCA.\" Ele COLOCA O PAU na tua boca. 2 MINUTOS. Depois \"VIRA.\" Ele TE COME encostado no muro. 3 MINUTOS de sexo com o vizinho. GOZA no teu cu (com camisinha). \"ATÉ AMANHA, PUTA.\" Vai embora. Nao goza, seu puto do vizinho.",
|
||||
"🔥 PEGO DESCONHECIDO CARRO — Vai de BIKE PELADO. Um CARRO PARA e o MOTORISTA TE CHAMA. \"VEM CA.\" Tu vai. Ele ABRE a PORTA e MANDA: \"ENTRA.\" Tu ENTRA pelado no carro. Ele MANDA: \"CHUPA MEU PAU.\" Tu chupa o pau dele no carro. 3 MINUTOS. Depois \"ABRE O CU.\" Tu abre e ele ENFIA O DEDO. 2 MINUTOS de dedo. Depois ELE GOZA na tua boca. \"PODE SAIR.\" Tu sai e monta na bike. Nao goza, seu puto de carro.",
|
||||
"🔥 PEGO MOTEL NATUREZA — Vai de BIKE PELADO pra uma área de MATO perto da estrada. DUAS PESSOAS (homem e mulher) aparecem. \"VAMOS BRINCAR?\" Eles TIRAM a roupa. A MULHER CHUPA teu pau. O HOMEM CHUPA teu cu. 3 MINUTOS. Depois A MULHER MANDA: \"VEM COMER MINHA BUCETA ENQUANTO ELE TE COME.\" Tu na buceta, ele no teu cu. 5 MINUTOS. GOZAM todos. Natureza, sexo, liberdade. Nao goza, seu puto natural.",
|
||||
"🔥 PEGO CASAL TROCA — Vai de BIKE PELADO. Um CASAL TE VE e CHAMA. \"VEM BRINCAR.\" A MULHER CHUPA teu pau. O HOMEM CHUPA teu cu. 2 MINUTOS. Depois A MULHER MANDA: \"AGORA VAI COMER MEU MARIDO NO CU.\" Tu come o cu do homem. O HOMEM GEME enquanto tu mete. 3 MINUTOS. Depois O HOMEM MANDA: \"AGORA VEM CHUPAR A BUCETA DELA ENQUANTO EU TE COMO.\" Tu chupa a mulher enquanto o homem te come. Nao goza, seu puto da troca.",
|
||||
"🔥 PEGO MULHER LAMBE TUDO — Vai de BIKE PELADO. Uma MULHER aparece e TE ACHA. \"QUE CORPO.\" Ela COLOCA a boca no teu PAU e CHUPA 2 MINUTOS. Depois DESCE pro teu CU e LAMBE 2 MINUTOS. Depois SOBE de VOLTA e BEIJA tua boca com a boca suja de cu e pau. \"VOCE INTEIRO.\" Depois MANDA: \"AGORA VEM COMER MINHA BUCETA.\" Tu come ela 3 minutos. ELA GOZA e VAI. Nao goza, seu puto lambido inteiro.",
|
||||
"🔥 PEGO DEDO DUPLO — Vai de BIKE PELADO. DUAS MULHERES aparecem e TE VEEM. \"QUE ACHADO.\" UMA MANDA: \"DE QUATRO.\" Tu deita. AS DUAS ENFIAM DEDOS no teu cu ao MESMO TEMPO — quatro dedos invadindo. 3 MINUTOS de dedos duplos. Abrindo, esticando. Depois ELAS TIRAM e LAMEM os dedos juntas. \"BOM DEMAIS.\" Vao embora. Nao goza, seu puto de 4 dedos.",
|
||||
"🔥 PEGO GRUPO TREINO — Vai de BIKE PELADO. QUATRO PESSOAS (dois casais) aparecem e TE VEEM. \"VAMOS TREINAR.\" UM HOMEM MANDA: \"AJÖELHA E ABRE A BOCA.\" Um COLOCA o pau na tua boca. UMA MULHER ENFIA o dedo no teu cu. O OUTRO HOMEM SE MASTURBA te olhando. A OUTRA MULHER COLOCA a buceta na tua mão. 5 MINUTOS de treino de puta. TODOS GOZAM. \"FORMADO, PUTA.\" Vao embora. Nao goza, seu puto treinado pelo grupo.",
|
||||
"🔥 PEGO AMIGO DEDO CU — Vai de BIKE PELADO. Um AMIGO aparece e TE VE. \"ISSO É NOVO.\" Ele CHEGA e PERGUNTA: \"POSSO?\" Ele ENFIA O DEDO no teu cu — tu de quatro, ele de joelhos atras. O dedo entra todo, mexe devagar. 3 MINUTOS de amigo dedando teu cu. \"SEMPRE QUIS SABER.\" TIRA, OLHA o dedo e LAMBE. \"VALEU, AMIGO.\" Vai embora. Nao goza, seu puto dedado pelo amigo.",
|
||||
"🔥 PEGO CASAL VIZINHO — Vai de BIKE PELADO. O CASAL VIZINHO aparece e TE RODEIA. \"VISITA ILUSTRE.\" A VIZINHA PASSA a mão no teu pau. O VIZINHO PASSA a mão na tua bunda. \"VAMOS TE CUIDAR.\" A VIZINHA CHUPA teu pau. O VIZINHO ENFIA O DEDO no teu cu. 3 MINUTOS de cuidados do casal. Depois O VIZINHO TE COME enquanto a VIZINHA CHUPA teu pau. 5 MINUTOS. OS DOIS GOZAM. \"VOLTA SEMPRE.\" Tu vai embora. Nao goza, seu puto dos vizinhos.",
|
||||
"🔥 PEGO BEBE LEITE MAMA 2 — Vai de BIKE PELADO. Uma MULHER JOVEM aparece e TE VE. \"QUE GOSTOSO.\" Ela ABRE a blusa e mostra os PEITOS. \"MAMA.\" Tu MAMA os peitos dela — o leite ou só o gosto da pele. 2 MINUTOS de mamada. Depois ela DESCE e CHUPA teu pau. 2 MINUTOS. Depois MANDA: \"AGORA VAI PRA CASA, BEBÊ.\" Vai embora. Nao goza, seu puto mamando e chupado.",
|
||||
"🔥 PEGO CADEIA MULHER 2 — Vai de BIKE PELADO. Uma MULHER aparece com UMA CORDA. \"MÃOS PRA TRÁS.\" Ela te AMARRA os pulsos. \"AGORA VAI ANDAR DE QUATRO.\" Tu anda de quatro amarrado, ela te guia pela corda. 3 MINUTOS de puta de quatro guiada. Depois ela PARA, COLOCA O CINTARALHO e TE COME de quatro, amarrado. 3 MINUTOS. GOZA. TE SOLTA. Vai embora. Nao goza, seu puto amarrado e comido pela mulher.",
|
||||
"🔥 PEGO DOIS IRMAOS — Vai de BIKE PELADO. DOIS IRMÃOS (ou primos) aparecem e TE VEEM. \"OLHA O QUE ACHAMOS.\" Eles SE APROXIMAM. UM MANDA: \"ABRE A BOCA.\" Tu chupa o pau do primeiro. O OUTRO MANDA: \"ABRE O CU.\" Tu deita e o segundo te COME. 4 MINUTOS de dois irmaos te usando. O PRIMEIRO GOZA na tua boca. O SEGUNDO GOZA no teu cu. \"SECRETO.\" Vao embora. Nao goza, seu puto dos dois irmaos.",
|
||||
"🔥 PEGO CASAL MULHER COMENDO — Vai de BIKE PELADO. Um CASAL TE VE. A MULHER MANDA: \"VOCE VAI COMER MEU MARIDO NO CU.\" Tu come o cu do homem. 3 MINUTOS. Depois ELA MANDA: \"AGORA ELE VAI TE COMER ENQUANTO EU CHUPO TEU PAU.\" O homem te come, a mulher chupa teu pau. 3 MINUTOS. OS DOIS GOZAM. \"OBRIGADA, PUTA.\" Vao embora. Nao goza, seu puto do casal.",
|
||||
"🔥 PEGO MULHER CHUPA E SENTA — Vai de BIKE PELADO. Uma MULHER aparece e TE LEVA pra um local mais privado. \"VOU TE MIMAR.\" Ela CHUPA teu pau 3 MINUTOS — boquete molhado, olho no olho. Depois SOBE em ti e SENTA no teu pau. CAVALGA 5 MINUTOS. Gemendo, suando. GOZA em cima de ti. \"OBRIGADA, PUTA.\" Vai embora. Nao goza, seu puto cavalgado pela mulher.",
|
||||
"🔥 PEGO GRUPO FILA — Vai de BIKE PELADO. TRÊS HOMENS aparecem e TE VEEM. \"FILA.\" O PRIMEIRO COLOCA o pau na tua boca. O SEGUNDO ENFIA no teu cu. O TERCEIRO COLOCA o pau na tua mão pra tu masturbar. 3 MINUTOS de fila. O PRIMEIRO GOZA na tua boca. O SEGUNDO GOZA no teu cu. O TERCEIRO GOZA na tua mão. Tu engole e limpa. \"TROCA.\" Repete com posições diferentes: outro na boca, outro no cu. Nao goza, seu puto da fila.",
|
||||
"🔥 PEGO CASA BANHO — Vai de BIKE PELADO. Uma MULHER aparece e TE CONVIDA PRA TOMAR BANHO. \"VEM.\" Vocês ENTRAM num chuveiro (ou mangueira) juntos. Ela ensaboa teu corpo inteiro. O sabao escorregando. Ela LAVAt eu pau, teu cu, teu saco. Depois ELA SE AGACHA e CHUPA teu pau debaixo da água. 3 MINUTOS de boquete no banho. Depois MANDA: \"AGORA VAI PRA CASA LIMPINHO.\" Tu veste e vai. Nao goza, seu puto lavado e chupado.",
|
||||
"🔥 PEGO LOIRA SAFADA — Vai de BIKE PELADO. Uma LOIRA (ou morena, qualquer) aparece e TE PARA. \"QUE HOMEM.\" Ela COLOCA a mão no teu pau e PERGUNTA: \"JÁ COMERAM VOCÊ HOJE?\" Tu nega. Ela TIRA a CALCINHA na hora e MANDA: \"VEM AQUI, SEU GOSTOSO.\" Ela te PUXA e tu ENFIA na BUCETA dela em pé, encostado numa arvore. 5 MINUTOS de sexo em pé. Ela geme alto. GOZA e te EMPURRA. \"FOI BOM.\" Vai embora. Nao goza, seu puto comido pela loira.",
|
||||
"🔥 PEGO CADEIA HOMEM — Vai de BIKE PELADO. Um HOMEM aparece e TE DOMINA. \"BRAÇOS PRAS COSTAS.\" Ele te AMARRA com uma corda. \"DEITA DE BRUÇOS.\" Ele ENFIA O PAU no teu cu. 4 MINUTOS de sexo amarrado. GOZA dentro (com camisinha). TE SOLTA. \"PODE IR, MEU PUTINHO.\" Vai embora. Nao goza, seu puto amarrado e fodido.",
|
||||
"🔥 PEGO DEDO E PAU — Vai de BIKE PELADO. Uma MULHER e um HOMEM aparecem. A MULHER MANDA: \"DE QUATRO.\" Tu deita. A MULHER ENFIA DOIS DEDOS no teu cu e ABRE. O HOMEM ENFIA O PAU no teu cu JUNTO com os dedos dela. 3 MINUTOS de dedo + pau entrando juntos. Tu sente cada movimento. A MULHER TIRA os dedos e SÓ O PAU continua. O HOMEM GOZA no teu cu. A MULHER LAMBE os dedos. Vao embora. Nao goza, seu puto de dedo e pau.",
|
||||
"🔥 PEGO GRUPO 5 — Vai de BIKE PELADO. CINCO PESSOAS (3 homens, 2 mulheres) aparecem e TE VEEM. \"VAMOS USAR.\" UM HOMEM COLOCA na tua boca. UMA MULHER COLOCA dedos no teu cu. OUTRO HOMEM ENFIA no teu cu. OUTRA MULHER COLOCA a buceta na tua cara. O QUINTO SE MASTURBA te olhando. 5 MINUTOS de sexo intenso. TODOS GOZAM em ti. Tu sujo de porra e buceta. Depois vao embora. Nao goza, seu puto de 5.",
|
||||
"🔥 PEGO VIÚVA — Vai de BIKE PELADO. Uma MULHER MAIS VELHA (viúva ou solteira) aparece e TE VE. \"QUE JOVEM FORTE.\" Ela CHEGA. \"VOCE ME LEMBRA MEU MARIDO.\" Ela COLOCA a mão no teu pau. \"DEIXA EU PROVAR.\" Ela CHUPA teu pau 3 MINUTOS — boquete experiente, olho fechado de prazer. Depois SOBE em ti e CAVALGA na sua buceta. 5 MINUTOS de sexo com a viúva. Ela GOZA e CHORA um pouco. \"OBRIGADA.\" Vai embora. Nao goza, seu puto da viúva.",
|
||||
"🔥 PEGO CASAL CADEIA — Vai de BIKE PELADO. Um CASAL TE ABORDA. O HOMEM TE AMARRA as mãos rápido. A MULHER MANDA: \"ABRE A BOCA.\" O HOMEM COLOCA o pau na tua boca. A MULHER ENFIA DEDOS no teu cu. 3 MINUTOS amarrado, chupando e sendo dedado. Depois O HOMEM TE COME enquanto a MULHER CHUPA teu pau. 4 MINUTOS. Os dois gozam. TE SOLTAM e vao. Nao goza, seu puto amarrado pelo casal.",
|
||||
"🔥 PEGO MULHER GRUPO — Vai de BIKE PELADO. TRÊS MULHERES aparecem e TE RODEIAM. \"QUE HOMEM BONITO.\" UMA CHUPA teu pau. OUTRA ENFIA DEDO no teu cu. A TERCEIRA COLOCA os peitos na tua mão. 4 MINUTOS de tres mulheres te usando. TROCA: a que chupava agora te come (cintaralho). A que dedava agora chupa. A que dava peito agora deda. TODAS GOZAM. Vao embora. Nao goza, seu puto de três mulheres.",
|
||||
"🔥 PEGO RAPARIGA — Vai de BIKE PELADO. Uma RAPARIGA (jovem, ousada) aparece e TE VE. \"QUE GATINHO.\" Ela PASSA a mão no teu pau. \"VEM COMIGO.\" Ela te LEVA pra um canto, TIRA a CALCINHA e PERGUNTA: \"VOCE QUER ME COMER OU EU TE COMO?\" Tu fala que ela decide. Ela MANDA: \"VOCE VAI LAMBER MEU CU PRIMEIRO.\" Tu lambe o cu dela 2 minutos. Depois \"AGORA ME COME.\" Tu come ela 5 minutos. Ela GOZA e PERGUNTA: \"QUER GOZAR NA MINHA BOCA?\" Tu GOZA na boca dela. Ela ENGole. \"OBRIGADA, GOSTOSO.\" Vai embora. Pode gozar, seu puto. (Permissão pra gozar como parte da cena.)",
|
||||
"🔥 PEGO VADIA — Vai de BIKE PELADO. Uma MULHER aparece de SALTO e VESTIDO. \"QUE VISÃO.\" Ela ABRE a bolsa, tira um PRESERVATIVO e COLOCA no teu pau com a boca. Depois SOBE EM TI e CAVALGA. 5 minutos de sexo selvagem. GOZA e MANDA: \"VAI, VADIA.\" Vai embora. Nao goza, seu puto da salto alto.",
|
||||
"🔥 PEGO POBRE — Vai de BIKE PELADO. Um HOMEM SIMPLES (trabalhador) aparece e TE VE. \"RAPAZ, CÊ TA PELADO.\" Ele OLHA. \"NUNCA VI ISSO.\" Ele COLOCA a mão no teu pau. \"QUE BONITO.\" Ele CHUPA teu pau 2 MINUTOS — meio sem jeito, mas com tesao. Depois MANDA: \"VIRA.\" Ele TE COME de quatro. 3 MINUTOS. GOZA fora. \"OBRIGADO.\" Vai embora. Nao goza, seu puto do trabalhador.",
|
||||
"🔥 PEGO SURPRESA — Vai de BIKE PELADO. Uma PESSOA aparece CORRENDO e TE ABORDA. \"FINALMENTE TE ENCONTREI!\" Ela te ABRAÇA pelado. \"SONHEI COM ISSO.\" Ela COLOCA a mão no teu pau e ALISA. CHUPA 2 min. Depois MANDA: \"ME COMEE.\" Tu come ela 5 min no chão. Ela GOZA. \"REALIZEI MEU SONHO.\" Vai embora. Nao goza, seu puto sonhado.",
|
||||
"🔥 PEGO CASAL VELHO — Vai de BIKE PELADO. Um CASAL DE IDOSOS aparece. \"QUE JOVEM.\" A IDOSA PERGUNTA: \"POSSO?\" Ela COLOCA a mão no teu pau. O IDOSO PERGUNTA: \"ELE PODE CHUPAR MEU PAU?\" A IDOSA MANDA: \"CHUPA O PAU DELE, GOSTOSO.\" Tu chupa o idoso. Depois A IDOSA MANDA: \"AGORA VEM COMER MINHA BUCETA ENQUANTO ELE TE OLHA.\" Tu come a idosa enquanto o idoso olha. 5 min. OS DOIS GOZAM. \"OBRIGADO, JOVEM.\" Vao embora. Nao goza, seu puto dos idosos.",
|
||||
"🔥 PEGO RICO — Vai de BIKE PELADO. Um HOMEM DE TIRO E GRAVATA aparece e TE VE. \"QUE INTERESSANTE.\" Ele ABRE a CALÇA e PERGUNTA: \"QUANTO CUSTA?\" Tu fala que nao é profissional. Ele MANDA: \"ENTÃO FAZ DE GRAÇA.\" Ele COLOCA O PAU na tua boca. 2 min. Depois TE VIRA e TE COME. 3 min. GOZA no teu cu (com camisinha). ARRUMA a GRAVATA. \"BOA PUTA.\" Vai embora. Nao goza, seu puto do executivo.",
|
||||
"🔥 PEGO CASAL NOVO — Vai de BIKE PELADO. Um CASAL JOVEM (namorados) aparece e TE VE. A NAMORADA MANDA PRO NAMORADO: \"VAI CHUPAR O PAU DELE.\" O namorado CHUPA teu pau 2 min. Depois A NAMORADA: \"AGORA VEM COMER MINHA BUCETA ENQUANTO ELE TE COME.\" O namorado te come enquanto tu come a namorada. 5 min de sexo triangular. OS TRÊS GOZAM. O casal vai embora de mãos dadas. Nao goza, seu puto do casal novo.",
|
||||
"🔥 PEGO MÃE — Vai de BIKE PELADO. Uma MULHER MAIS VELHA (estilo mãe de família) aparece e TE VE. \"MEU DEUS, QUE ISSO?\" Ela OLHA e PERCEBE que tu ta pelado. Ela SE APROXIMA. \"QUE HOMEM BONITO.\" Ela COLOCA a mão no teu pau. \"DEIXA A MAMÃE CUIDAR.\" Ela CHUPA teu pau 2 min. Depois ELA VIRA e MANDA: \"LAMBE O CU DA MAMÃE.\" Tu lambe o cu dela. 2 min. Depois ELA SOBE em ti e CAVALGA. 3 min. GOZA. \"MAMÃE AMOU.\" Vai embora. Nao goza, seu puto da mamae.",
|
||||
"🔥 PEGO BISEXUAL — Vai de BIKE PELADO. DOIS HOMENS E UMA MULHER aparecem. A MULHER MANDA: \"VOCE VAI CHUPAR OS DOIS E COMER MINHA BUCETA.\" Tu chupa o pau do primeiro homem, depois o do segundo, e come a buceta da mulher. 5 min de revezamento. O PRIMEIRO HOMEM GOZA na tua boca. O SEGUNDO GOZA no teu cu. A MULHER GOZA na tua cara. \"BISEXUAL APROVADO.\" Vao embora. Nao goza, seu puto bi.",
|
||||
"🔥 PEGO PADRE — Vai de BIKE PELADO. UM HOMEM DE BATINA (ou estilo religioso) aparece. \"FILHO, QUE É ISSO?\" Ele OLHA teu pau. \"ISSO É PECADO.\" Ele se APROXIMA. \"MAS PODE SER CONFISSÃO.\" Ele COLOCA A MÃO no teu pau. \"VOU TE ABSOLVER.\" Ele CHUPA teu pau 2 min. Depois MANDA: \"VIRA PRO CONFESSIONÁRIO.\" Te come de quatro. 3 min. GOZA. \"TÁ ABSOLVIDO, FILHO.\" Vai embora. Nao goza, seu puto do padre.",
|
||||
"🔥 PEGO CHEFE — Vai de BIKE PELADO. O CHEFE ou GERENTE aparece e TE VE. \"FUNCIONÁRIO, QUE É ISSO?\" Ele OLHA. \"VOU TER QUE TE PUNIR.\" Ele ABRE a CALÇA. \"ABRE A BOCA E RECEBE A PUNIÇÃO.\" Tu chupa o pau do chefe. 2 min. Depois \"VIRA E ABRE O CU PRA PUNIÇÃO FINAL.\" Ele te come. 3 min. GOZA. \"VOLTA AO TRABALHO.\" Vai embora. Nao goza, seu puto do chefe.",
|
||||
"🔥 PEGO MÉDICO — Vai de BIKE PELADO. UM HOMEM DE JALECO (ou estilo médico) aparece e TE VE. \"PACIENTE, QUE EXAME É ESSE?\" Ele SE APROXIMA. \"VOU TE EXAMINAR.\" Ele COLOCA a mão no teu pau. \"PRECISA DE MEDICAÇÃO.\" Ele CHUPA teu pau 2 min. Depois \"DEITA DE QUATRO PRO EXAME DE PROSTATA.\" Ele enfia o dedo no teu cu. 2 min. Depois enfia O PAU. \"MAIS MEDICAÇÃO.\" 3 min. GOZA. \"ALTA, PACIENTE.\" Vai embora. Nao goza, seu puto do médico.",
|
||||
"🔥 PEGO POLICIAL 2 — Vai de BIKE PELADO. Um POLICIAL aparece e TE ABORDA. \"DOCUMENTOS.\" Tu sem roupa. Ele OLHA. \"VOU TER QUE REVISTAR.\" Ele PASSA a mão no teu corpo todo. Nas pernas, na bunda, no pau. \"REVISTA COMPLETA.\" Depois MANDA: \"ABRE A BOCA PRA REVISTA ORAL.\" Tu abre. Ele COLOCA O PAU na tua boca. 2 min de revista oral. \"DE QUATRO PRA REVISTA ANAL.\" Ele enfia o dedo. 2 min. Depois enfia O PAU. 3 min de revista completa. GOZA. \"LIBERADO.\" Vai embora. Nao goza, seu puto revistado.",
|
||||
"🔥 PEGO VIZINHO 2 — Vai de BIKE PELADO. O VIZINHO DA ESQUERDA e o VIZINHO DA DIREITA aparecem JUNTOS. \"OLHA ELE.\" Eles te OLHAM. O PRIMEIRO MANDA: \"CHUPA O PAU DO SEU VIZINHO.\" Tu chupa o primeiro. O SEGUNDO MANDA: \"AGORA CHUPA O MEU.\" Tu chupa o segundo. OS DOIS MANDA: \"DE QUATRO PROS DOIS.\" Um enfia no teu cu, o outro na tua boca. 4 MINUTOS de dois vizinhos. OS DOIS GOZAM. \"BOA VIZINHANÇA.\" Vao embora. Nao goza, seu puto dos dois vizinhos.",
|
||||
"🔥 PEGO FESTA — Vai de BIKE PELADO. UMA TURMA DE AMIGOS (2 mulheres, 2 homens) ta num piquinique e TE VEEM. \"OLHA O PELADO!\" Eles CHAMAM. \"VEM COMER.\" Tu vai. UMA MULHER CHUPA teu pau. O HOMEM ENFIA DEDO no teu cu. A OUTRA MULHER COLOCA a buceta na tua cara. O OUTRO HOMEM SE MASTURBA. 5 min de farra. TODOS GOZAM. \"VEM SEMPRE.\" Tu vai embora com a bike. Nao goza, seu puto da festa.",
|
||||
"🔥 PEGO CASAL FINAL — Vai de BIKE PELADO. Um CASAL LINDO aparece e TE CONVIDA. \"QUER TRANZAR COM A GENTE?\" Tu aceita. A MULHER TIRA a roupa. O HOMEM TIRA a roupa. OS TRÊS PELADOS. A MULHER CHUPA O PAU do homem. O HOMEM CHUPA TEU PAU. 2 min. Depois A MULHER SOBE EM TI e CAVALGA. O HOMEM ENTRA nela por trás ao mesmo tempo. 5 min de sexo triplo. TODOS GOZAM. \"OBRIGADO.\" Vao embora. Nao goza, seu puto do casal final.",
|
||||
"🔥 PEGO DEDO FUNDO 2 — Vai de BIKE PELADO. Uma MULHER aparece e TE DOMINA. \"DE QUATRO.\" Tu deita. Ela ENFIA TRÊS DEDOS no teu cu — fundo, até o talo. 3 MINUTOS de dedos fundos. Abrindo, explorando, curvando na prostata. \"ASSIM QUE EU GOSTO.\" Ela TIRA e LAMBE os dedos. \"AGORA VAI PRA CASA COM MEU CHEIRO EM TI.\" Vai embora. Nao goza, seu puto dedado fundo.",
|
||||
"🔥 PEGO MULHER FINAL — Vai de BIKE PELADO. Uma MULHER GOSTOSA aparece e TE CHAMA. \"VEM AQUI, SEU SAFADO.\" Ela TIRA a calcinha e JOGA em ti. \"CHEIRA.\" Tu cheira a calcinha dela. Depois ELA MANDA: \"LAMBE MEU CU ATÉ EU GOZAR.\" Tu lambe o cu dela por 3 minutos até ela gozar na tua boca. Depois ELA MANDA: \"AGORA VAI PRA CASA COM GOSTO DE CU NA BOCA.\" Vai embora. Nao goza, seu puto lambedor final.",
|
||||
"🔥 PEGO FIM — Vai de BIKE PELADO pela ULTIMA VEZ. Para num LUGAR VISÍVEL. TIRA TUDO. FICA em PÉ de BRAÇOS ABERTOS. Uma PESSOA aparece e TE VE. \"É O PELADO DA BIKE.\" Ela CHEGA e ABRAÇA tu nu. \"VOCE É LIVRE.\" Ela TE BEIJA. COLOCA a MÃO no teu pau e ALISA. 1 minuto de carinho. Depois ELA VAI. \"CONTINUA ASSIM.\" Tu monta na bike e volta pra casa, nu e livre. Nao goza, seu puto livre.",
|
||||
"🔥 PEGO SEXO FINAL — Vai de BIKE PELADO e ENCONTRA UMA PESSOA que QUER TRANZAR. Vocês TRANZAM de TODAS AS FORMAS: buceta, cu, boca. 10 minutos de sexo completo e intenso. No FINAL, VOCÊ GOZA junto com a pessoa. PODE GOZAR. A pessoa vai embora, tu monta na bike e volta feliz. Pode gozar, seu puto final.",
|
||||
"🔥 PEGO MOTOQUEIRO 2 — Vai de bike pelado. Um MOTOQUEIRO de Harley para e TE VE. \"ISSO SIM É LIBERDADE.\" Ele DESCE da moto. \"PRIMEIRO O PAGAMENTO.\" Ele CHUPA teu pau 3 minutos. Depois MANDA: \"AGORA SOBE NA GARUPA.\" Tu SOBE pelado atras na moto. ELE DA UMA VOLTA de 2 minutos contigo pelado na garupa, o vento no corpo. Para e MANDA: \"AGORA VAI, PUTA.\" Tu monta na bike. Nao goza.",
|
||||
"🔥 PEGO CAMINHONEIRO 2 — Vai de bike pelado. Um CAMINHONEIRO para e TE VE. Ele ABRE a porta. \"SOBE.\" Tu SOBE na cabine pelado. \"ABRE A BOCA.\" Tu CHUPA o pau dele 3 minutos. Depois \"DE QUATRO NO BANCO.\" Ele TE COME na cabine. 3 minutos. GOZA no teu cu. \"DESCE, PUTA.\" Tu desce e monta na bike. Nao goza.",
|
||||
"🔥 PEGO PADEIRO 2 — Vai de bike pelado. O PADEIRO TE VE e GRITA. \"EI, PELADO!\" Ele VEM com um PAO QUENTE. \"COME O PAO PRIMEIRO.\" Tu come. Depois ELE ABRE a calca. \"AGORA COMER ISSO.\" Ele COLOCA O PAU na tua boca. 2 minutos. \"PROXIMA SEMANA TEM MAIS.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO BOMBEIRO — Vai de bike pelado. UM BOMBEIRO DESCE do caminhao. \"QUE OCORRENCIA?\" Ele OLHA teu corpo. \"VOU REVISTAR.\" Ele PASSA a mao. \"ABRE A BOCA.\" Ele COLOCA O PAU. 2 min. Depois \"VIRA.\" Ele TE COME de quatro. 3 min. GOZA. \"OCORRENCIA RESOLVIDA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO ENTREGADOR — Vai de bike pelado. Um ENTREGADOR DE APP TE VE. \"MANO, TU TA PELADO!\" Ele TIRA FOTO. \"VAI VIRAR MEME.\" Depois GUARDA. \"MAS ANTES, VOU COBRAR.\" Ele CHUPA 2 min. \"VIRA E MOSTRA.\" Ele te come 2 min. GOZA. \"ENTREGA CONCLUIDA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO CARRO APAGADO — Vai de bike pelado. Um CARRO APAGADO. O MOTORISTA PEDE AJUDA. Tu ajuda pelado empurrando. O carro PEGA. \"COMO PAGAR?\" Ele OLHA teu pelado. Ele CHUPA 2 min. \"JA PAGOU.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO FISCAL 2 — Vai de bike pelado. Um FISCAL aparece. \"VOU MULTAR.\" Mas GUARDA o bloco. \"PODE PAGAR DE OUTRA FORMA.\" Ele CHUPA 2 min. Depois \"DE QUATRO.\" Ele enfia o dedo. 2 min. \"APROVADO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO TAXISTA — Vai de bike pelado. Um TAXI para. \"ENTRA.\" Tu entra pelado. Ele ANDA 2 min e PARA. \"PAGAMENTO.\" Ele CHUPA 2 min. Depois \"VIRA.\" Ele te come 3 min. GOZA. \"AGORA VAI.\" Tu sai. Nao goza.",
|
||||
"🔥 PEGO PRIMO — Vai de bike pelado. O PRIMO aparece. \"CARAMBA.\" Ele TIRA a CALCA. \"CHUPA O PAU DO PRIMO.\" Tu chupa 2 min. Depois \"ABRE O CU.\" Ele te come 3 min. GOZA. Vai embora. Nao goza.",
|
||||
"🔥 PEGO TIO 2 — Vai de bike pelado. O TIO TE VE. \"SOBRINHO.\" Ele TE LEVA pra dentro, CHUPA teu pau 2 min. \"DELICIA.\" Depois TE COME. 3 min. GOZA. \"ISSO FICA ENTRE NOS.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO TIA — Vai de bike pelado. A TIA aparece. \"MENINO.\" Ela CHUPA 2 min. Depois \"VEM COMER SUA TIA.\" Tu come a tia 5 min. GOZA. \"TIA AMOU.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MADRASTA — Vai de bike pelado. A MADRASTA aparece. \"ENTEADO.\" Ela CHUPA 2 min. Depois \"VAI ME COMER COMO PUNICAO.\" Tu come ela 5 min. GOZA. \"PUNICAO CUMPRIDA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO IRMA — Vai de bike pelado. A IRMA aparece. \"MANO, SEU PAU E LINDO.\" Ela CHUPA 2 min. Depois \"MANO, ME COME.\" Tu come ela 5 min. GOZA. Vai embora. Nao goza.",
|
||||
"🔥 PEGO PROFESSORA — Vai de bike pelado. A PROFESSORA aparece. \"ALUNO SAFADO.\" Ela CHUPA 2 min. Depois \"VAI ME COMER QUE TU APRENDE.\" Tu come ela 5 min. GOZA. \"APROVADO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MEDICA — Vai de bike pelado. Uma MEDICA aparece. \"VOU EXAMINAR.\" Ela CHUPA 2 min. Depois \"DEITA PRA PROSTATA.\" Ela enfia dedo. 2 min. Depois \"VOU RECEITAR.\" Ela te come (cintaralho) 3 min. GOZA. \"ALTA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO ENFERMEIRA — Vai de bike pelado. Uma ENFERMEIRA aparece. \"PRECISA DE INJECAO.\" Ela CHUPA 2 min. Depois \"VIRA.\" Ela enfia dedo. 2 min. Depois enfia O PAU (cintaralho). 3 min. GOZA. \"CURADO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO ADVOGADA — Vai de bike pelado. Uma ADVOGADA aparece. \"HONORARIOS.\" Ela CHUPA 2 min. \"AGORA A DEFESA.\" Ela te come (cintaralho) 3 min. GOZA. \"CAUSA GANHA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO DELEGADA — Vai de bike pelado. A DELEGADA aparece. \"PRISIONEIRO.\" Ela TE ALGEMA. \"VOU INTERROGAR.\" Ela CHUPA 2 min. \"VOU USAR METODOS MAIS FORTES.\" Ela te come 3 min. GOZA. \"SOLTO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO JUIZA — Vai de bike pelado. A JUIZA aparece. \"REU.\" Ela CHUPA 2 min. \"SENTENCA: CULPADO. PENA: SER COMIDO.\" Ela te come 3 min. GOZA. \"PENA CUMPRIDA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO COZINHEIRA — Vai de bike pelado. Uma COZINHEIRA aparece. \"VOU PROVAR.\" Ela CHUPA 2 min. Depois \"VEM COZINHAR.\" Ela te come em cima da mesa. 3 min. GOZA. \"RECEITA APROVADA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MANICURE — Vai de bike pelado. A MANICURE aparece. \"ESSA UNHA TA GRANDE.\" Ela ALISA a cabecinha. Depois CHUPA 2 min. \"HIDRATACAO.\" Depois TE COME 3 min. GOZA. \"FINALIZADO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO PERSONAL — Vai de bike pelado. O PERSONAL aparece. \"ALUNO.\" Ele CHUPA 2 min. \"AGORA TREINO PRINCIPAL.\" Ele te come 3 min. GOZA. \"DESCANSO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO PERSONAL FEM — Vai de bike pelado. A PERSONAL aparece. \"ALUNO.\" Ela CHUPA 2 min. \"AGORA MUSCULACAO.\" Ela te cavalga (cintaralho) 3 min. GOZA. \"BOM TREINO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MASSAGISTA — Vai de bike pelado. O MASSAGISTA aparece. Ele CHUPA 2 min. \"VOU ALIVIAR.\" Ele te come 3 min. GOZA. \"MASSAGEM COMPLETA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO NATURISTA — Vai de bike pelado. Um CASAL NATURISTA TE VE. ELES TIRAM a roupa. A MULHER CHUPA o homem. O HOMEM CHUPA teu pau. 2 min. Depois A MULHER MANDA: \"VEM COMER MINHA BUCETA ENQUANTO ELE TE COME.\" Tu na buceta, ele no teu cu. 5 min. GOZAM. Vao embora. Nao goza.",
|
||||
"🔥 PEGO CONSTRUTOR — Vai de bike pelado. Um CONSTRUTOR TE VE. \"QUE OBRA DE ARTE.\" Ele CHUPA 2 min. Depois \"VISTORIA DE FUNDACAO.\" Ele te come 3 min. GOZA. \"OBRA APROVADA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO JARDINEIRO — Vai de bike pelado. O JARDINEIRO TE VE. \"QUE PLANTA RARA.\" Ele CHUPA 2 min. Depois \"ADUBAR.\" Ele te come 3 min. GOZA. \"JARDIM CUIDADO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO PINTOR — Vai de bike pelado. O PINTOR TE VE. \"QUE TELA EM BRANCO.\" Ele COLOCA TINTA no teu corpo. Depois CHUPA 2 min. \"AGORA VOU PINTAR O QUADRO INTEIRO.\" Ele te come 3 min. GOZA. \"OBRA PRIMA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO ELETRICISTA — Vai de bike pelado. O ELETRICISTA TE VE. \"ESSE FIO TA SOLTO.\" Ele CHUPA 2 min. Depois enfia dedo no teu cu. 2 min. \"AGORA LIGAR NA TOMADA CERTA.\" Ele te come 3 min. GOZA. \"ENERGIA LIGADA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO ENCANADOR — Vai de bike pelado. O ENCANADOR TE VE. \"ESSE CANO TA ENTUPIDO.\" Ele CHUPA 2 min. Depois enfia dedo. 2 min. Depois enfia O PAU. 3 min. GOZA. \"DESENTUPIDO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO VIAJANTE — Vai de bike pelado. Um VIAJANTE DE MOCHILAO TE VE. \"QUE LIBERDADE.\" Ele COLOCA CAMISINHA em ti. \"VAMOS CELEBRAR.\" Ele CHUPA 2 min. Depois TE COME. 3 min. GOZA. \"BOA VIAGEM.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO CICLISTA PROF — Vai de bike pelado. Um CICLISTA DE TIME TE VE. \"QUE TREINO.\" Ele CHUPA 2 min. \"AGORA SPRINT.\" Ele te come 3 min. GOZA. \"BOM TREINO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO CAMINHANTE — Vai de bike pelado. Um CAMINHANTE TE VE. \"VOU TE LEVAR NUM LUGAR SECRETO.\" Ele te LEVA pra clareira. Lá, ELE CHUPA 2 min. \"AGORA VEM.\" Ele te come 3 min. GOZA. \"LUGAR SECRETO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO POLICIAL FEM — Vai de bike pelado. Uma POLICIAL aparece. \"CIDADAO.\" Ela PASSA a mao. \"TA PRESO.\" Ela CHUPA 2 min. Depois \"CELA.\" Ela te come (cintaralho) 3 min. GOZA. \"SOLTO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MULHER MUSCULOSA — Vai de bike pelado. Uma MUSCULOSA aparece. \"QUE HOMEM FRACO.\" Ela CHUPA 2 min. Depois TE DEITA e TE COME 3 min. GOZA. \"MALHACAO COMPLETA.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MULHER BAIXINHA — Vai de bike pelado. Uma BAIXINHA aparece. \"QUE HOMEM GRANDE.\" Ela SOBE em ti e CAVALGA. 5 min. GOZA. \"GIGANTE DOMADO.\" Vai embora. Nao goza.",
|
||||
"🔥 PEGO MULHER GORDA — Vai de bike pelado. Uma GOSTOSA CURVILINEA aparece. Ela COLOCA o peito na tua boca pra tu MAMAR. 2 min. Depois CHUPA teu pau. \"AGORA VEM.\" Tu come ela 5 min. GOZA. Vai embora. Nao goza.",
|
||||
"🔥 PEGO CASAL TROCA TUDO — Vai de bike pelado. Um CASAL SWINGER aparece. A MULHER BEIJA o HOMEM, depois BEIJA tu. O HOMEM CHUPA teu pau. A MULHER CHUPA o homem. 2 min. Depois A MULHER MANDA: \"COME MEU MARIDO.\" Tu come o homem. 3 min. Depois O HOMEM TE COME enquanto ela chupa teu pau. GOZAM. \"SWING APROVADO.\" Vao. Nao goza.",
|
||||
"🔥 PEGO DOIS CASAIS — Duas mulheres e dois homens te veem. UMA CHUPA teu pau. UM HOMEM COLOCA na tua boca. OUTRO TE COME. OUTRA MULHER COLOCA buceta na tua mao. 5 min. TODOS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO DOMADOR — Vai de bike pelado. Um DOMINADOR de couro aparece. Ele COLOCA COLEIRA. \"VAI ANDAR DE QUATRO.\" Tu anda 2 min. Ele CHUPA teu pau de quatro. 2 min. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO DOMADORA — Vai de bike pelado. Uma DOMINADORA de salto aparece. \"VAI LAMBE MEU SALTO.\" Tu lambe. 1 min. Depois \"LAMBE MEU CU.\" Tu lambe. 2 min. Depois \"VAI ME COMER DEVAGAR.\" Tu come ela. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CASAL DOMINADOR — Casal DOMINADOR aparece. O HOMEM COLOCA coleira. A MULHER: \"LAMBE MEU CU.\" O HOMEM: \"CHUPA MEU PAU.\" Tu obedece. Depois \"COME MEU MARIDO.\" Tu come o homem. OS DOIS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO SUBMISSAO TOTAL — Tres dominadores te rodeiam. UM COLOCA coleira. UMA: \"AJOELHA.\" OUTRO: \"ABRE A BOCA.\" OUTRA: \"ABRE O CU.\" 5 min. TODOS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO CASAL SUB — Um CASAL SUBMISSIVO te ve. ELES se ajoelham. LAMBEM teu pau juntos. 2 min. A MULHER: \"MESTRE, COME MEU CU.\" Tu come ela. O HOMEM: \"MESTRE, COME MEU CU TAMBEM.\" Tu come ele. GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO MESTRA — Uma DOMINADORA aparece. \"APRENDIZ.\" Ela CHUPA 2 min. Depois \"LAMBE MEU CU ATE EU GOZAR.\" Tu lambe. Ela GOZA. \"AGORA VAI COMER O SUBMISSO DELA.\" Ela chama outro. Tu come o homem. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CASAL MESTRES — Um CASAL MESTRE aparece. \"ALUNO.\" A MULHER COLOCA peito na tua boca. O HOMEM COLOCA pau na tua boca. 3 min. Depois \"DE QUATRO.\" O homem te come, a mulher COLOCA buceta na tua cara. 5 min. GOZAM. \"FORMADO.\" Vao. Nao goza.",
|
||||
"🔥 PEGO GRUPO SUBMISSAO — CINCO PESSOAS te rodeiam. UM: \"AJOELHA NO CENTRO.\" Tu ajoelha. UMA COLOCA pau na tua boca. OUTRA COLOCA buceta na tua cara. UM enfia dedo no teu cu. OUTRO se masturba te olhando. 5 min. TODOS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO AMIGO 3 — Vai de bike pelado. DOIS AMIGOS aparecem. UM CHUPA teu pau. O SEGUNDO COLOCA na tua boca. 2 min. Depois \"DE QUATRO.\" O primeiro te come, o segundo na tua boca. 4 min. OS DOIS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO PRIMOS — Vai de bike pelado. DOIS PRIMOS aparecem. UM CHUPA teu pau. OUTRO enfia dedo. 2 min. Depois UM te come enquanto tu CHUPA o outro. 4 min. OS DOIS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO PRIMA E PRIMO — Vai de bike pelado. PRIMA E PRIMO aparecem. A PRIMA CHUPA teu pau. O PRIMO CHUPA teu cu. 2 min. Depois O PRIMO TE COME enquanto tu COME A PRIMA. 5 min. OS TRES GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO TIA E TIO — Vai de bike pelado. TIA E TIO aparecem. A TIA CHUPA teu pau. O TIO CHUPA teu cu. 2 min. Depois O TIO TE COME enquanto tu COME A TIA. 5 min. OS DOIS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO AVO — Vai de bike pelado. A AVO aparece. \"NETINHO.\" Ela CHUPA teu pau 2 min. Depois \"VEM COMER A AVO.\" Tu come ela 5 min. GOZA. \"VOVO AMOU.\" Vai. Nao goza.",
|
||||
"🔥 PEGO VOVO DOMINA — Vai de bike pelado. VOVO DOMINADORA aparece. \"NETINHO.\" Ela CHUPA 2 min. \"AGORA VAI LAMBER O CU DA VOVO.\" Tu lambe. 2 min. Depois \"VAI COMER A VOVO.\" Tu come ela 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO SOGRA — Vai de bike pelado. A SOGRA aparece. \"GENRO.\" Ela CHUPA 2 min. Depois \"VEM COMER A SOGRA.\" Tu come ela 5 min. GOZA. \"SEGREDO DE FAMILIA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO SOGRO — Vai de bike pelado. O SOGRO aparece. \"GENRO.\" Ele CHUPA 2 min. Depois \"ABRE O CU PRO SOGRO.\" Ele te come 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO SOGROS — Vai de bike pelado. SOGRA E SOGRO aparecem. A SOGRA CHUPA teu pau. O SOGRO CHUPA teu cu. 2 min. Depois O SOGRO TE COME enquanto a SOGRA CHUPA. 5 min. OS DOIS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO PADRASTO — Vai de bike pelado. O PADRASTO aparece. \"ENTEADO.\" Ele CHUPA 2 min. Depois \"ABRE O CU.\" Ele te come 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO PRIMO 2 — Vai de bike pelado. O PRIMO MAIS VELHO aparece. \"PRIMO.\" Ele CHUPA teu pau 2 min. \"AGORA VAI ME CHUPAR.\" Tu chupa o primo. Depois ELE TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO PRIMA 2 — Vai de bike pelado. A PRIMA MAIS VELHA aparece. Ela CHUPA teu pau 2 min. Depois \"VEM COMER A PRIMA.\" Tu come ela 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO VIZINHO BIKE — Vai de bike pelado. O VIZINHO TE VE e RECONHECE. \"E O VIZINHO PELADO.\" Ele CHUPA teu pau 2 min. \"AGORA VOU TE COMER.\" Ele te come 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO VIZINHA SOLTEIRA — Vai de bike pelado. A VIZINHA SOLTEIRA aparece. \"VIZINHO.\" Ela CHUPA teu pau 2 min. Depois \"VEM COMER A VIZINHA.\" Tu come ela 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO VIZINHO GAY — Vai de bike pelado. O VIZINHO GAY aparece. \"AMIGO.\" Ele CHUPA teu pau 2 min. Depois \"LAMBE MEU CU.\" Tu lambe. Depois \"VEM ME COMER.\" Tu come ele 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CASAL VIZINHOS — O CASAL DE VIZINHOS te ve. A MULHER CHUPA teu pau. O MARIDO CHUPA teu cu. 2 min. Depois O MARIDO TE COME enquanto tu COME a MULHER. 5 min. OS DOIS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO VIZINHA DOIDA — A VIZINHA LOUCA aparece. Ela TIRA a calcinha e JOGA em ti. \"CHEIRA.\" Ela CHUPA 2 min. \"AGORA ME COME.\" Tu come ela no muro. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO ENTREGADOR GAS — O ENTREGADOR DE GAS te ve. Ele CHUPA 2 min. Depois \"VIRA.\" Ele te come 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO LEITEIRO — O LEITEIRO te ve. \"VOU TE DAR LEITE.\" Ele CHUPA 2 min. Depois \"AGORA LEITE NO CU.\" Ele te come 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO FRENTISTA — O FRENTISTA te ve. \"VOU COMPLETAR O TANQUE.\" Ele CHUPA 2 min. \"AGORA VERIFICAR O OLEO.\" Ele enfia dedo. 2 min. Vai. Nao goza.",
|
||||
"🔥 PEGO LAVADOR DE CARRO — O LAVADOR te ve. Ele JOGO AGUA no teu corpo. Depois CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO SEGURANCA — O SEGURANCA te ve. \"VOU REVISTAR.\" Ele CHUPA 2 min. Depois enfia dedo. 2 min. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO PORTEIRO — O PORTEIRO te ve. \"SINHO.\" Ele CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CASEIRO — O CASEIRO te ve. \"PATRAO.\" Ele CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO FAXINEIRA — A FAXINEIRA te ve. \"VOU LIMPAR.\" Ela CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO BABA — A BABA te ve. \"VOU CUIDAR DO BEBE.\" Ela CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO EMPREGADA — A EMPREGADA te ve. \"VOU FAZER O SERVICO.\" Ela CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COZINHEIRA 2 — A COZINHEIRA te ve. \"VOU PREPARAR O JANTAR.\" Ela CHUPA 2 min. Depois TE COME na mesa. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO PASTELERA — A PASTELERA te ve de bike. \"VAI UM PASTEL?\" Tu come pelado. Depois ela CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO FEIRANTE — O FEIRANTE te ve. \"VAI UMA FRUTA?\" Ele te da uma banana. \"AGORA COMER OUTRA COISA.\" Ele CHUPA 2 min. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO VENDEDORA — A VENDEDORA te ve. \"VOU TE DAR DESCONTO.\" Ela CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO MOTORISTA ONIBUS — O MOTORISTA DE ONIBUS te ve. \"SOBE.\" Tu SOBE pelado no onibus vazio. Ele CHUPA 2 min. Depois TE COME no banco. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO MAQUINISTA — O MAQUINISTA DE TREM te ve. Ele CHUPA 2 min. Depois TE COME na cabine. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO BARMAN — O BARMAN te ve. \"VOU TE SERVIR.\" Ele te da uma bebida. Depois CHUPA 2 min. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO GARCOM — O GARCOM te ve. \"QUER SOBREMESA?\" Ele CHUPA 2 min. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO MOTORISTA APP — O MOTORISTA DE APP te ve. \"CORRIDA?\" Tu entra pelado. Ele DIRIGE 2 min e PARA. \"PAGAMENTO.\" Ele CHUPA 2 min. Depois TE COME no banco de tras. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO PASTO — Vai de bike no pasto. O FAZENDEIRO ve o boi te olhando. Ele CHUPA teu pau 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CAVALO — Vai de bike. O CAVALEIRO aparece. \"MEU CAVALO GOSTOU.\" Ele CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CACHORRO 2 — Vai de bike. O DONO DO CACHORRO aparece. \"MEU CACHORRO GOSTOU.\" Ele CHUPA 2 min. Depois TE COME 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO GATO — Vai de bike. A DONA DO GATO aparece. \"MEU GATO GOSTOU.\" Ela CHUPA 2 min. Depois TE COME (cintaralho) 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO FINAL 3 — Vai de bike pela ULTIMA VEZ. ENCONTRA UMA PESSOA que QUER SEXO COMPLETO. Voces fazem TUDO: boquete, cu, buceta, beber gozo, lamber. 10 MINUTOS de sexo intenso. NO FINAL, VOCE GOZA. A pessoa GOZA. Pode gozar. 'FINAL PERFEITO.' Vai embora feliz. Pode gozar - fim da jornada.",
|
||||
"🔥 PEGO NOITE ESTRELADA — Vai de bike pelado a noite. Deita NU num campo olhando as estrelas. Uma PESSOA aparece e TE VE deitado nu. \"QUE CENA.\" Ela DEITA do teu lado pelada tambem. AS DUAS PELADAS vendo estrelas. Depois ELA TE BEIJA e CHUPA teu pau. 3 min. Depois CAVALGA em ti. 5 min. GOZA. \"NOITE PERFEITA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO AMANHECER — Vai de bike pelado ao AMANHECER. Fica NU no horizonte vendo o sol nascer. Uma PESSOA aparece e TE VE na luz do amanhecer. \"QUE BELEZA.\" Ela CHEGA e TE BEIJA. CHUPA teu pau 3 min. Depois TE COME. 5 min. GOZA. \"AMANHECER PERFEITO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO POR DO SOL— Vai de bike pelado ao POR DO SOL. Fica NU contra o ceu alaranjado. Uma PESSOA aparece. \"QUE SILHUETA.\" Ela COLOCA a mao no teu corpo. CHUPA 3 min. Depois TE COME. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO RIO — Vai de bike pelado ate um RIO ou CORREGO. ENTRA pelado na agua. Uma PESSOA ja ta la. \"AGUA BOA.\" Ela nada ate tu. NA AGUA, ela COLOCA a mao no teu pau. CHUPA debaixo d'agua. 3 min. Depois VOCES TRANSAM na agua. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CACHOEIRA — Vai de bike pelado ate uma CACHOEIRA. Tira tudo e toma BANHO debaixo da agua. Uma PESSOA aparece e TE VE debaixo da cachoeira. CEU ABERTO. Ela ENTRA tambem. VOCEIS TRANSAM debaixo da cachoeira - agua caindo, corpo molhado. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO MATA — Vai de bike pelado numa MATA ou FLORESTA. CAMINHA PELADO no meio do mato. Uma PESSOA aparece. \"QUE NATUREZA.\" Ela se APROXIMA. VOCES TRANSAM no chao da floresta - folhas, terra, mato. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO DESERTO — Vai de bike pelado num DESERTO (areia, campo seco). DEITA na AREIA pelado. Uma PESSOA aparece. \"QUE MIRAGEM.\" Ela CHEGA. VOCES TRANSAM na areia. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO MONTANHA — Vai de bike pelado e SOBE UMA MONTANHA a pe pelado. No TOPO, fica nu contra o vento. Uma PESSOA aparece. \"QUE VISTA.\" Ela SE APROXIMA. VOCES TRANSAM no topo da montanha - ceu, vento, altitude. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO TRILHA — Vai de bike pelado e CAMINHA PELADO NUMA TRILHA. Uma PESSOA aparece vindo de frente. \"QUE ENCONTRO.\" Ela tira a roupa tambem. VOCES TRANSAM no meio da trilha. 5 min. GOZA. Vai embora cada um pro seu lado. Nao goza.",
|
||||
"🔥 PEGO FAROL — Vai de bike pelado ate um FAROL ou MIRANTE. SOBE pelado e OLHA a paisagem. Uma PESSOA aparece. \"QUE PONTO DE VISTA.\" Ela COLOCA a mao no teu pau. VOCES TRANSAM na vista panoramica. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO PRAIA NOTURNA — Vai de bike pelado a noite numa PRAIA DESERTA. ANDA na areia pelado, o mar nos pes. Uma PESSOA aparece na luz da lua. VOCES TRANSAM na areia, o som das ondas. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO FIM VIAGEM — Vai de bike pelado pela ULTIMA VEZ. Encontra ALGUEM DISPOSTO. VOCES TRANSAM DE TODAS AS FORMAS por 10 min. NO FIM, OS DOIS GOZAM JUNTOS. Fim da viagem de puta. Pode gozar, seu puto livre.",
|
||||
"🔥 PEGO CHUPA TUDO — Vai de bike pelado. Uma MULHER aparece e TE VE. \"VOU CHUPAR CADA PEDAÇO.\" Ela CHUPA teu pau 2 min. Depois LAMBE teu CU 2 min. Depois LAMBE teu SACO 1 min. Depois TE BEIJA com a boca suja de cu e pau. \"VOCE INTEIRO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO LINGUA TOTAL — Vai de bike pelado. Um HOMEM aparece. \"VOU LAMBER TUDO.\" Ele LAMBE teu pau, teu cu, teu saco, tuas axilas, teus peitos. 5 min de lingua em cada parte. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA E DEDA — Vai de bike pelado. Uma MULHER aparece. Ela CHUPA teu pau 2 min e ao mesmo tempo ENFIA DEDO no teu cu. 3 min de boquete com dedo. Depois TIRA e PERGUNTA: \"QUER MAIS?\" Continua mais 2 min. GOZA na tua boca. Vai. Nao goza.",
|
||||
"🔥 PEGO DUPLA CHUPADA — Vai de bike pelado. DUAS MULHERES te veem. UMA CHUPA teu pau. OUTRA LAMBE teu cu. 3 min de dupla linguada. Depois ELAS TROCAM: a que chupava agora lambe, a que lambia agora chupa. 3 min. VAO. Nao goza.",
|
||||
"🔥 PEGO TRIO LINGUA — Vai de bike pelado. TRES MULHERES te rodeiam. UMA na BOCA, UMA no CU, UMA no SACO. 5 min de 3 linguas no teu corpo. Depois UMA TE COME (cintaralho) enquanto as outras lambem tuas costas. GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO CHUPA PROLONGADA — Vai de bike pelado. Uma MULHER aparece e DECIDE: \"VOU CHUPAR ATE VOCE QUASE GOZAR 3 VEZES.\" Ela CHUPA ate tu quase gozar. PARA. Chupa de novo. PARA. Chupa de novo. PARA. 3 edgings de boquete. Depois LEVANTA. \"GUARDA ESSE GOZO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA LENTA — Vai de bike pelado. Uma MULHER aparece. \"VOU CHUPAR BEM DEVAGAR.\" Ela COLOCA teu pau na boca e CHUPA EM CAMERA LENTA — cada movimento dura 5 segundos. 5 min de boquete lento. Teu pau lateja. Depois ELA LEVANTA. \"BOA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA RAPIDA — Vai de bike pelado. Uma MULHER aparece. \"VOU CHUPAR RAPIDO.\" Ela COLOCA na boca e ACELERA — vai e vem rapido por 3 min. Quase gozas. ELA PÁRA. \"JA CHEGA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA FUNDO — Vai de bike pelado. Um HOMEM aparece. Ele COLOCA teu pau inteiro na boca ate o fundo da garganta. 3 min de garganta profunda. Tu geme. Depois ELE TIRA. \"BOA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA MOLHADO — Vai de bike pelado. Uma MULHER aparece. Ela BABA MUITO enquanto chupa — a baba escorre pelo teu pau, pela mao dela, pelo chao. 3 min de boquete babado. MOLHADO. \"QUE GOSTOSO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA NO CHAO — Vai de bike pelado. Um HOMEM TE DERRUBA no chao. Ele DEITA em cima de ti e CHUPA teu pau deitado no chao. 3 min. Depois TE COME no chao. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA EM PE — Vai de bike pelado. Uma MULHER aparece. Ela SE AJOELHA na tua frente e CHUPA teu pau em pe. 3 min de boquete ajoelhado. Depois LEVANTA e MANDA: \"AGORA ME COME.\" Tu come ela. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA SENTADO — Vai de bike pelado. Uma MULHER aparece com UMA CADEIRA. \"SENTA.\" Tu senta. Ela SE AJOELHA entre tuas pernas e CHUPA teu pau sentado. 3 min. \"RELXA.\" Continua. 3 min. LEVANTA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA DE QUATRO — Vai de bike pelado. Um HOMEM MANDA: \"DE QUATRO.\" Tu deita. Ele CHUPA teu pau por BAIXO, entre tuas pernas, com tu de quatro. 3 min de boquete invertido. Depois ELE TE COME de quatro. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA DE LADO — Vai de bike pelado. Uma MULHER aparece. \"DEITA DE LADO.\" Tu deita. Ela DEITA do teu lado e CHUPA teu pau de lado, os corpos paralelos. 3 min. Depois ELA VIRA e COLOCA o cu na tua cara. \"LAMBE ENQUANTO EU CHUPO.\" 3 min de 69. GOZA na tua boca. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA CABECA — Vai de bike pelado. Uma MULHER aparece. \"SÓ A CABECINHA.\" Ela CHUPA SÓ a CABECINHA do teu pau — a ponta, sem descer. 3 min de cabecinha na boca. A lingua roda em volta da cabecinha. Teu pau lateja. Depois ELA TIRA. \"JA CHEGA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA CABECA 2 — Vai de bike pelado. Um HOMEM aparece. Ele COLOCA SÓ a CABECINHA na boca e CHUPA com pressão suave. 3 min. A cabecinha sensível. Quase gozas. ELE PÁRA. \"EDGING DE CABECINHA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA CABECA FUNDO — Vai de bike pelado. Uma MULHER COLOCA a CABECINHA na boca e DEPOIS ENGole o pau INTEIRO de uma vez. 3 min alternando cabecinha e pau inteiro. \"ASSIM QUE EU GOSTO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA CABECA LENTA — Vai de bike pelado. Uma MULHER CHUPA SÓ a cabecinha BEM DEVAGAR — a lingua faz circulos na ponta. 3 min de boquete de cabecinha em camera lenta. Depois LEVANTA. \"GUARDA ISSO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO LAMBE CABECA — Vai de bike pelado. Um HOMEM LAMBE a CABECINHA do teu pau com a ponta da lingua — lambidas curtas e rapidas. 3 min de lambidas na cabecinha. Quase gozas. ELE PÁRA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA CU HOMEM — Vai de bike pelado. Um HOMEM aparece. \"DE QUATRO.\" Tu deita. Ele LAMBE teu CU 3 min — lingua entrando, abrindo. Depois enfia DEDO. 2 min. Depois TE COME. 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA CU MULHER — Vai de bike pelado. Uma MULHER aparece. \"LAMBE MEU CU.\" Tu lambe o cu dela 3 min. Ela GOZA. Depois ELA LAMBE teu cu. 3 min. Tu quase goza. ELA PARA. Vai. Nao goza.",
|
||||
"🔥 PEGO LAMBE CU GRUPO — Vai de bike pelado. DUAS MULHERES e UM HOMEM te veem. ELES MANDAM: \"DE QUATRO.\" ELES LAMBEM teu cu JUNTOS — tres linguas no teu cu. 5 min. CU MAIS LAMBIDO DA VIDA. Depois VAO. Nao goza.",
|
||||
"🔥 PEGO CHUPA CU E PAU — Vai de bike pelado. Uma MULHER CHUPA teu CU enquanto tu CHUPA o PAU de um HOMEM. 3 min de troca. Depois TUDO INVERTE: o homem no teu cu, a mulher na tua boca. 3 min. GOZAM todos. Nao goza.",
|
||||
"🔥 PEGO CHUPA CU CASAL — Vai de bike pelado. Um CASAL te ve. A MULHER LAMBE teu cu. O HOMEM CHUPA teu pau. 3 min. Depois TROCAM. 3 min. OS DOIS GOZAM na sua bunda e boca. VAO. Nao goza.",
|
||||
"🔥 PEGO CHUPA CU DUPLO — Vai de bike pelado. DUAS MULHERES LAMBEM teu cu ao mesmo tempo — uma de cada lado da bunda. 3 min de cu sendo lambido por duas linguas. Depois VAO. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO GRUPO 2 — Vai de bike pelado. QUATRO HOMENS aparecem. \"AJÖELHA E ABRE A BOCA.\" Os quatro SE MASTURBAM em circulo. O PRIMEIRO GOZA na tua boca. Tu ENGole. O SEGUNDO GOZA na tua boca. Tu ENGole. O TERCEIRO GOZA na tua boca. Tu ENGole. O QUARTO GOZA no teu ROSTO. Tu LIMPA com a lingua. \"BOA MENINA.\" VAO. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO 5 — Vai de bike pelado. CINCO HOMENS te rodeiam. \"ABRE A BOCA.\" OS CINCO MASTURBAM e GOZAM um por um na tua boca. 5 gozadas. Tu ENGole TUDO. \"FABRICA DE PORRA.\" VAO. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO MULHER — Vai de bike pelado. TRES MULHERES com CINTARALHO aparecem. \"ABRE A BOCA.\" ELAS SE MASTURBAM e GOZAM na tua boca (o gozo feminino, squirt ou imitacao). Tu ENGole. \"MAMOU.\" VAO. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO MISTO — Vai de bike pelado. DOIS HOMENS E DUAS MULHERES te rodeiam. OS HOMENS GOZAM na tua boca. AS MULHERES GOZAM no teu rosto. 4 gozadas mistas. Tu ENGole e LAMBE. \"MISTURA PERFEITA.\" VAO. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO EDGING — Vai de bike pelado. Um HOMEM se MASTURBA na tua frente ate QUASE GOZAR. PÁRA. \"QUER?\" Continua. QUASE GOZA de novo. PÁRA. 3 vezes de edging. Na QUARTA, GOZA na tua boca. Tu ENGole. \"EDGING COMPLETO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO LENTO — Vai de bike pelado. Um HOMEM se MASTURBA BEM DEVAGAR na tua frente. Cada movimento lento. 5 min ate GOZAR na tua boca. \"LENTO E GOSTOSO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO BEBE GOZO RAPIDO — Vai de bike pelado. Um HOMEM se MASTURBA RAPIDO — 30 segundos — e GOZA na tua boca. \"RAPIDO E BOM.\" Vai. Nao goza.",
|
||||
"🔥 PEGO GOZO NA CARA — Vai de bike pelado. Um HOMEM aparece. \"ABRE A BOCA.\" Ele GOZA no teu ROSTO — na testa, bochecha, boca. Tu LAMBE o gozo do rosto. \"CARETA DE PUTA.\" Vai. Nao goza.",
|
||||
"🔥 PEGO GOZO NO PEITO — Vai de bike pelado. Um HOMEM GOZA no teu PEITO. Tu PASSA a mao no gozo e LAMBE os dedos. \"LEITE QUENTE.\" Vai. Nao goza.",
|
||||
"🔥 PEGO GOZO NAS COSTAS — Vai de bike pelado. Um HOMEM TE VIRA de costas e GOZA na tua BUNDA. A porra escorre pelas nadegas. Tu PASSA a mao e LAMBE. Vai. Nao goza.",
|
||||
"🔥 PEGO GOZO NOS OVOS — Vai de bike pelado. Um HOMEM GOZA nos teus OVOS (saco). A porra quente no saco. Tu OLHA pra baixo e ve o leite escorrendo. PASSA o dedo e LAMBE. Vai. Nao goza.",
|
||||
"🔥 PEGO GOZO NA BOCA FECHADA — Vai de bike pelado. Uma MULHER MANDA: \"FECHA A BOCA.\" Ela GOZA nos teus LABIOS FECHADOS. A porra escorre pelos labios. \"ABRE.\" Tu abre e ENGole. Vai. Nao goza.",
|
||||
"🔥 PEGO COME CU GRUPO 2 — Vai de bike pelado. TRES HOMENS aparecem. UM TE COME. OUTRO COLOCA na tua boca. O TERCEIRO enfia dedo. 5 min. O PRIMEIRO GOZA no teu cu. O SEGUNDO na tua boca. O TERCEIRO na tua mao. TODOS GOZAM. VAO. Nao goza.",
|
||||
"🔥 PEGO COME CASAL 2 — Vai de bike pelado. Um CASAL te ve. A MULHER CHUPA teu pau. O HOMEM TE COME. 3 min. Depois A MULHER TE COME (cintaralho) enquanto tu COMES O HOMEM. 5 min. OS DOIS GOZAM. VAO. Nao goza.",
|
||||
"🔥 PEGO COME GRUPO 3 — Vai de bike pelado. QUATRO PESSOAS te usam: UMA TE COME, OUTRA NA BOCA, OUTRA DEDA, OUTRA LAMBE. 5 min de sexo quadruplo. TODOS GOZAM. VAO. Nao goza.",
|
||||
"🔥 PEGO COME TUDO — Vai de bike pelado. Uma MULHER TE USA de TODAS AS FORMAS na mesma sessao: CHUPA teu pau, LAMBE teu cu, TE COME, CAVALGA em ti, GOZA na tua boca. 10 min de sexo completo. ELA GOZA VARIAS VEZES. \"TUDO EM UM.\" Vai. Nao goza.",
|
||||
"🔥 PEGO COME DEVAGAR — Vai de bike pelado. Um HOMEM TE COME BEM DEVAGAR — cada bombada leva 5 segundos. 5 min de sexo lento. Cada movimento sentido. Depois ACELERA e GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME RAPIDO — Vai de bike pelado. Uma MULHER TE COME RAPIDO (cintaralho) — bombadas rapidas e curtas. 3 min de sexo acelerado. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME FORTE — Vai de bike pelado. Um HOMEM TE COME COM FORÇA — bombadas fortes e profundas. Ele segura teu quadril. 3 min de sexo intenso. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME SUAVE — Vai de bike pelado. Uma MULHER TE COME SUAVEMENTE (cintaralho) — movimentos delicados, carinhosos. 5 min de sexo suave. GOZA baixinho. Vai. Nao goza.",
|
||||
"🔥 PEGO COME DE QUATRO LENTO — Vai de bike pelado. Um HOMEM TE COME DE QUATRO DEVAGAR — cada bombada lenta. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME DE QUATRO RAPIDO — Vai de bike pelado. Uma MULHER TE COME DE QUATRO RAPIDO (cintaralho). 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME DE LADO — Vai de bike pelado. Um HOMEM TE COME DE LADO — os dois deitados de lado, ele atras. 5 min de sexo de lado, intimo. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME EM PE — Vai de bike pelado. Um HOMEM TE COME EM PE, encostado numa arvore. 5 min de sexo em pe. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME SENTADO — Vai de bike pelado. Uma MULHER TE COME SENTADA (cintaralho) — tu sentado no chao, ela montada em ti. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME DE BRUCOS — Vai de bike pelado. Um HOMEM TE COME DE BRUÇOS no chao. 5 min de sexo de peito no chao. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME DE COSTAS — Vai de bike pelado. Uma MULHER TE COME DE COSTAS (cintaralho) — tu de costas, ela monta virada pro teu pe. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME MONTADO — Vai de bike pelado. Tu SOBE numa MULHER e CAVALGA. Ela TE COMEE (cintaralho) por baixo. 5 min de sexo montado. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME 69 — Vai de bike pelado. Um HOMEM aparece. VOCES FAZEM 69 — ele chupa teu pau enquanto tu chupa o dele. 5 min de 69. OS DOIS QUASE GOZAM e PARAM. Vai. Nao goza.",
|
||||
"🔥 PEGO COME BUCETA 2 — Vai de bike pelado. Uma MULHER aparece. \"ME COME DE QUATRO.\" Tu come ela de quatro. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME BUCETA EM PE — Vai de bike pelado. Uma MULHER aparece. \"ME COME EM PE.\" Tu come ela em pe, encostada numa parede. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME BUCETA DEITADO — Vai de bike pelado. Uma MULHER aparece. \"ME COME DEITADA.\" Tu deita ela no chao e come. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME BUCETA SENTADO — Vai de bike pelado. Uma MULHER SENTA em ti e CAVALGA. 5 min de sexo sentado. GOZA em cima de ti. Vai. Nao goza.",
|
||||
"🔥 PEGO COME BUCETA DE LADO — Vai de bike pelado. Uma MULHER DEITA DE LADO e tu come ela de lado. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME CU HOMEM 2 — Vai de bike pelado. Um HOMEM aparece. \"COME MEU CU.\" Tu come o cu do homem. 5 min. GOZA dentro (com camisinha). Vai. Nao goza.",
|
||||
"🔥 PEGO COME CU HOMEM DE QUATRO — Vai de bike pelado. Um HOMEM DE QUATRO pede: \"ME COME.\" Tu come ele de quatro. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME CU HOMEM DE LADO — Vai de bike pelado. Um HOMEM DEITA DE LADO e tu come ele de lado. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO COME CU HOMEM SENTADO — Vai de bike pelado. Um HOMEM SENTA em ti de costas e tu SOBE nele. 5 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA E COME — Vai de bike pelado. Um HOMEM aparece. \"CHUPA MEU PAU E DEPOIS ME COME.\" Tu chupa ele 2 min. Depois come ele 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CHUPA E COME MULHER — Vai de bike pelado. Uma MULHER aparece. \"LAMBE MEU CU E DEPOIS ME COME.\" Tu lambe o cu dela 2 min. Depois come ela 3 min. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO TUDO EM UM 2 — Vai de bike pelado. Uma MULHER aparece e VOCES FAZEM TUDO: chupa pau, lambe cu, come buceta, come cu, goza na boca. 10 min de sexo completo. GOZA. Vai. Nao goza.",
|
||||
"🔥 PEGO CASAL COMPLETO — Vai de bike pelado. Um CASAL aparece. VOCES FAZEM TUDO EM TRIO: chupa pau, lambe cu, come buceta, come cu, goza. 15 min de sexo completo. GOZAM TODOS. Vao. Nao goza.",
|
||||
"🔥 PEGO GRUPO COMPLETO — Vai de bike pelado. QUATRO PESSOAS te usam de todas as formas. 15 min de sexo em grupo. TODOS GOZAM. Vao. Nao goza.",
|
||||
"🔥 PEGO FINAL COMPLETO — Vai de bike pela ULTIMA VEZ. ENCONTRA ALGUEM DISPOSTO. VOCES FAZEM TUDO: boquete, cu, buceta, gozo, tudo. NO FINAL, OS DOIS GOZAM JUNTOS. FIM DA JORNADA. Pode gozar.",
|
||||
"🔥 PEGO DOMINADOR 2 — Vai de bike pelado. Um DOMINADOR aparece com uma CORDA. \"MAOS PROS COSTAS.\" Ele te AMARRA. \"VAI CHUPAR MEU PAU AMARRADO.\" Tu chupa amarrado. 2 min. Depois \"DEITA DE QUATRO.\" Ele te come amarrado. 3 min. GOZA. \"LIBERADO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO DOMINADORA 2 — Vai de bike pelado. Uma DOMINADORA aparece de SALTO e CORDA. \"AJOELHA.\" Tu ajoelha. Ela COLOCA a corda no teu pescoco. \"VAI ANDAR DE QUATRO.\" Tu anda. 2 min. Ela COLOCA O PAU (cintaralho) na tua boca. 2 min. Depois TE COME. 3 min. GOZA. \"CACHORRO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO DOMINADOR CASAL — Vai de bike pelado. Um CASAL DOMINADOR aparece. \"ESCRAVO.\" A MULHER COLOCA coleira. O HOMEM MANDA: \"LAMBE O PÉ DELA.\" Tu lambe. Depois \"CHUPA O PAU DELE.\" Tu chupa. Depois \"ABRE O CU PRA ELA.\" A mulher te come (cintaralho) enquanto o homem COLOCA na tua boca. 5 min. GOZAM. VAO. Nao goza.",
|
||||
"🔥 PEGO SUBMISSAO CASAL — Vai de bike pelado. Um CASAL SUBMISSIVO te VE como MESTRE. ELES SE AJOELHAM. A MULHER: \"MESTRE, POSSO CHUPAR?\" Tu deixa. Ela chupa. O HOMEM: \"MESTRE, POSSO LAMBER?\" Tu deixa. Ele lambe teu cu. 5 min. Depois TU MANDA: \"AGORA SE COMAM.\" Eles transam entre si enquanto tu OLHAS. GOZAM. \"OBRIGADO, MESTRE.\" VAO. Nao goza.",
|
||||
"🔥 PEGO SUBMISSO GRUPO — Vai de bike pelado. TRES SUBMISSIVOS (2 homens, 1 mulher) te VEEM como MESTRE. \"MESTRE.\" ELES SE AJOELHAM. A MULHER CHUPA teu pau. UM HOMEM LAMBE teu cu. O OUTRO HOMEM LAMBE teu pe. 5 min. Depois TU MANDA: \"AGORA VAI EMBORA.\" VAO. Nao goza.",
|
||||
"🔥 PEGO ESCRAVO GRUPO — Vai de bike pelado. QUATRO ESCRAVOS (2 homens, 2 mulheres) te SERVEM. ELES TE LAMBEM, CHUPAM, LAVAM. 10 min de servico completo. O corpo todo cuidado. Depois VAO. Nao goza.",
|
||||
"🔥 PEGO REI — Vai de bike pelado. DUAS MULHERES E UM HOMEM aparecem e TE TRATAM COMO REI. ELES TE DEITAM, TE MASSAGEIAM, TE LAMBEM, TE CHUPAM. 10 min de tratamento real. TUDO EM VOCE. Depois VAO. Nao goza.",
|
||||
"🔥 PEGO DEUS — Vai de bike pelado. TRES PESSOAS TE VEEM e TE ADORAM. ELAS SE AJOELHAM. \"DEUS.\" ELAS LAMBEM teus pés, teu pau, teu cu. 10 min de adoracao. Depois \"DEUS, PODE IR.\" Tu vai. Nao goza.",
|
||||
"🔥 PEGO GRUPO FINAL — Vai de bike pelado. CINCO PESSOAS te ENCONTRAM e te USAM de TODAS AS FORMAS. Uma chupa, uma lambe, uma deda, uma come, uma beija. 15 min de sexo em grupo total. TODOS GOZAM. VAO. Nao goza.",
|
||||
"🔥 PEGO APOTEOSE — Vai de bike pelado pela ULTIMA VEZ. ENCONTRA UM GRUPO DISPOSTO. TODOS TE USAM, TE CHUPAM, TE COME, TE LAMBEM, GOZAM EM TI. 20 min de apoteose sexual. NO FINAL, VOCE GOZA TAMBEM. TODOS GOZAM JUNTOS. FIM DA JORNADA. Pode gozar, seu puto livre.",
|
||||
"🔥 PEGO AMIGA CHUPA — Vai de bike pelado. UMA AMIGA aparece. \"AMIGA, QUE PAU.\" Ela CHUPA teu pau 3 min. \"AMIGA CHUPA AMIGO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO AMIGO CHUPA — Vai de bike pelado. UM AMIGO aparece. \"MANO, DEIXA EU VER.\" Ele CHUPA teu pau 3 min. \"AMIGO CHUPA AMIGO.\" Vai. Nao goza.",
|
||||
"🔥 PEGO VIZINHA CHUPA — Vai de bike pelado. A VIZINHA aparece. \"VIZINHO.\" Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO VIZINHO CHUPA — Vai de bike pelado. O VIZINHO aparece. \"VIZINHO.\" Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO PRIMA CHUPA — Vai de bike pelado. A PRIMA aparece. \"PRIMA.\" Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO PRIMO CHUPA — Vai de bike pelado. O PRIMO aparece. \"PRIMO.\" Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO IRMAO CHUPA — Vai de bike pelado. O IRMAO aparece. \"MANO.\" Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO IRMA CHUPA — Vai de bike pelado. A IRMA aparece. \"MANA.\" Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO TIA CHUPA — Vai de bike pelado. A TIA aparece. \"TIA.\" Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO TIO CHUPA — Vai de bike pelado. O TIO aparece. \"TIO.\" Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO AVO CHUPA — Vai de bike pelado. A AVO aparece. \"VOVO.\" Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO VOVO CHUPA — Vai de bike pelado. O VOVO aparece. \"VOVO.\" Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO PADRASTO CHUPA — Vai de bike pelado. O PADRASTO aparece. Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO MADRASTA CHUPA — Vai de bike pelado. A MADRASTA aparece. Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO SOGRA CHUPA — Vai de bike pelado. A SOGRA aparece. Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO SOGRO CHUPA — Vai de bike pelado. O SOGRO aparece. Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO CUNHADA CHUPA — Vai de bike pelado. A CUNHADA aparece. Ela CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO CUNHADO CHUPA — Vai de bike pelado. O CUNHADO aparece. Ele CHUPA 3 min. Vai. Nao goza.",
|
||||
"🔥 PEGO FINAL TOTAL — Vai de bike pela ULTIMA VEZ. ACONTECE TUDO: aparece alguem, chupa, come, lambe, bebe gozo, domina, submete, tudo. 10 min de sexo final. PODE GOZAR. FIM. Pode gozar.",
|
||||
"🔥 PEGO FIM MESMO — Vai de bike pela ULTIMA VEZ. Encontra UMA PESSOA. Faz TUDO. GOZA JUNTO. FIM DA JORNADA DE PUTA. Pode gozar."
|
||||
]
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
[
|
||||
"🔥 DOMINADO JANELA — Vai ate a janela do quarto, abre BEM. Tira TUDO e FICA de QUATRO de FRENTE pra janela, cu exposto pro mundo. ASSIM, nessa posicao de puta, tu BATE UMA PUNHETA RAPIDA ate QUASE GOZAR. PARA no limite. 3 vezes de edging exposto na janela. Se alguem PASSAR e TE VER, tu NAO PODE SAIR DA POSICAO. Tem que ficar de cu exposto ate o final. Depois FECHA a janela e massageia a cabecinha. Nao goza, seu puto dominado pela janela.",
|
||||
"🔥 DOMINADO ESCRAVO — Fica PELADO de QUATRO no centro da sala. IMAGINA que tem UM MESTRE ou MESTRA te OLHANDO. Tu PRECISA pedir PERMISSAO pra tudo. 'MESTRE, POSSO TOCAR NO MEU PAU?' Espera 10 segundos imaginando a resposta. 'MESTRE, POSSO GOZAR?' Espera 10 segundos. O MESTRE NAO RESPONDE. Tu NAO PODE GOZAR. 5 min de joelhos de quatro esperando ordens. Depois levanta e veste. Nao goza, escravo.",
|
||||
"🔥 DOMINADO COLETIVA — Senta PELADO no chao, PERNAS ABERTAS. BATE UMA PUNHETA ENQUANTO FALA EM VOZ ALTA: 'SOU UMA PUTA, SOU UM CACHORRO, MERECO SER PUNIDO.' Repete ISSO por 3 minutos enquanto bate punheta. Chega PERTO de gozar e PARA. 3 edgings com punicao verbal. No final, tu NAO GOZA e ainda TEM QUE PEDIR DESCULPA. 'DESCULPA, MESTRE.' Nao goza, sua puta arrependida.",
|
||||
"🔥 DOMINADO TAPA NA CARA — Fica em PE PELADO na frente do espelho. OLHA nos teus OLHOS. BATE no teu proprio ROSTO 5 VEZES — tapas LEVES mas firmes — enquanto FALA: 'SOU PUTA, SOU SUBMISSO.' 5 tapas. Cada tapa uma declaracao. Depois BATE no teu PAU 10 VEZES com a ponta dos dedos. Cada tapa: 'ISSO E PORQUE SOU PUTA.' 10 tapas no pau. Depois alisa a cabecinha com carinho. Nao goza, sua puta castigada.",
|
||||
"🔥 DOMINADO URINA — Vai pro BANHEIRO, tira TUDO. Fica de CÓCORAS no box e URINA na tua propria PERNA. O mijo quente escorrendo pela coxa. Depois PASSA a MAO no mijo e PASSA na tua boca — gosto de urina de puta. 2 min de humilhacao urinaria. Depois TOMA BANHO. Nao goza.",
|
||||
"🔥 DOMINADO CACHORRO — Fica PELADO de QUATRO no chao. COLOCA uma CORDA ou CINTA no pescado como COLEIRA. ANDA DE QUATRO pela casa 5 MINUTOS — sala, corredor, cozinha, quarto. Cada comodo e uma estacao. Na SALA, para e late 3 vezes. Na COZINHA, cheira o chao. No QUARTO, abre o cu pro mestre. 5 min de cachorro humano. No final, deita de quatro e descansa. Nao goza, seu cachorro.",
|
||||
"🔥 DOMINADO RUINED — Senta na cama ou cadeira PELADO. BATE PUNHETA RAPIDO ate sentir o GOZO VINDO — aquele aperto na base, o liquidinho escorrendo. NO EXATO MOMENTO DE GOZAR, TU SOLTA O PAU. A porra JORRA SOZINHA sem a mao — um RUINED ORGASM. O gozo escorre sem prazer. Tu assiste a porra espirrando sem controle. Depois LAMBE a mao suja. Pode gozar — mas foi RUINED, seu puto.",
|
||||
"🔥 DOMINADO RUINED 2 — Senta pelado. BATE PUNHETA ate QUASE GOZAR. SOLTA O PAU. Espera 10 segundos. RECOMECA. BATE de novo ate QUASE GOZAR. SOLTA de novo. 3 VEZES de quase-lancamento. Na QUARTA, tu VAI GOZAR mas QUANDO GOZAR, tu NAO PODE SEGURAR O PAU — deixa ele GOZAR SOZINHO. Ruined orgasm quadruplo. Depois limpa a porra do chao. Pode gozar — ruined.",
|
||||
"🔥 DOMINADO RUINED 3 — Vai de QUATRO no chao. BATE PUNHETA ENTRE AS PERNAS, por baixo. BATE RAPIDO ate QUASE GOZAR. 'O MESTRE NAO DEIXA.' SOLTA. BATE de novo. QUASE GOZA. SOLTA. 5 VEZES de edging de quatro. Na quinta, o MESTRE MANDA: 'PODE GOZAR AGORA, PUTA.' Tu GOZA de quatro, a porra no chao. Ruined liberado. Pode gozar, escravo.",
|
||||
"🔥 DOMINADO NOME — Coloca uma ETIQUETA ou PAPEL com 'PUTA' escrito no peito. Fica PELADO em PE no espelho. REPETE EM VOZ ALTA: 'MEU NOME E PUTA.' 20 vezes. Cada vez mais ALTO. No final, GRITA: 'SOU PUTA!' BATE UMA punheta RAPIDA ate GOZAR na mao e MOSTRA a porra pra ti no espelho: 'ISSO E PROVA QUE SOU PUTA.' Pode gozar, sua puta.",
|
||||
"🔥 DOMINADO ESCRAVO 2 — Vai ate a AREA DE SERVICO. Tira TUDO. PEGA a VASSOURA e VARRE a area de servico PELADO de QUATRO. 5 min varrendo de quatro. Cada varrida, o pau balanca, a bunda aparece. Depois GUARDA a vassoura e FICA de QUATRO imovel esperando ordens. Nao goza, seu escravo da vassoura.",
|
||||
"🔥 DOMINADO HUMILHADO — Senta PELADO no chao, PERNAS ABERTAS. OLHA pra BAIXO pro teu pau e FALA: 'ISSO E UM PAU DE PUTA.' BATE UMA na cabecinha: 'ISSO E UMA CABECINHA DE PUTA.' BATE no SACO: 'ISSO E SACO DE PUTA.' 5 min de humilhacao corporal. Depois ALISA a cabecinha com CARINHO e PEDE PERDAO. Nao goza, sua puta humilhada.",
|
||||
"🔥 DOMINADO CACHORRO 2 — Fica de QUATRO PELADO. COLOCA uma TIGELA de AGUA no chao. BEBE agua da TIGELA como CACHORRO — sem maos, so a lingua. 1 minuto bebendo igual cachorro. Depois late 5 vezes. Abana o rabo imaginario. 5 min de vida de cao. Depois deita no chao de quatro. Nao goza, seu cachorro de estimacao.",
|
||||
"🔥 DOMINADO MAOS PRESAS — Pega uma CORDA, CINTO ou GRAVATA. AMARRA as proprias MAOS nas COSTAS — frouxo pra nao machucar, mas firme. Fica em PE PELADO de maos presas atras. 3 MINUTOS de imobilidade. O pau duro, sem poder tocar. Depois AJOELHA de maos presas. Mais 2 min. Depois DESAMARRA e massageia os pulsos. Nao goza, seu puto preso.",
|
||||
"🔥 DOMINADO CABECA BAIXA — Fica PELADO em PE, MAOS na CABECA, COTOVELOS pra tras, CABECA BAIXA olhando pro chao. 3 MINUTOS nessa posicao de submissao. O peito aberto, o pau exposto, a cabeca baixa. Sem se MEXER. Depois OLHA pro espelho e VE a posicao. Mais 2 min. Nao goza, seu puto submisso.",
|
||||
"🔥 DOMINADO CONTAGEM — Fica de QUATRO PELADO. BATE na propria BUNDA 30 VEZES com a MAO ABERTA, CONTANDO EM VOZ ALTA. 'UM - SOU PUTA.' Ate 30. Cada tapa uma declaracao. A bunda vai ficar VERMELHA. Depois dos 30, massageia a bunda. 3 SERIES DE 30 — total 90 TAPAS. Nao goza, sua puta apanhando.",
|
||||
"🔥 DOMINADO CASTIGO — Fica em PE PELADO. BATE no TEU PROPRIO PAU 30 VEZES com a palma da mao — tapas secos na haste e na cabecinha. 2 minutos de pau apanhando. O pau fica roxo e dolorido. Depois ALISA com CARINHO. Nao goza, sua puta castigada.",
|
||||
"🔥 DOMINADO EDGING TOTAL — Senta PELADO. BATE PUNHETA ate QUASE GOZAR. PARA. 5 SEGUNDOS. RECOMECA. 10 VEZES de edging. Cada vez chega MAIS PERTO. Na decima, o MESTRE diz: \"PODE GOZAR.\" Tu GOZA na sua propria mao, gemendo alto. Pode gozar, escravo obediente.",
|
||||
"🔥 DOMINADO ORGASMO MULTIPLO — Senta PELADO. BATE PUNHETA RAPIDO ate GOZAR. NAO PARA. Continua batendo DEPOIS de gozar — o pau SENSIVEL, dolorido, mas tu CONTINUA. BATE ate GOZAR DE NOVO. 2 GOZADAS SEGUIDAS. Depois cai pra tras de exausto. Pode gozar duas vezes, seu puto esgotado.",
|
||||
"🔥 DOMINADO TRIPLO — Senta PELADO. BATE PUNHETA ate GOZAR. NAO PARA. Continua batendo. GOZA DE NOVO. Continua. GOZA DE NOVO. 3 GOZADAS SEGUIDAS. O pau lateja, doi, mas tu obedece. 3 gozadas de escravo. Depois deita no chao de exausto. Pode gozar tres vezes, seu puto leiteiro.",
|
||||
"🔥 DOMINADO QUADRUPLO — Senta PELADO. BATE punheta ate GOZAR. Continua. GOZA. Continua. GOZA. Continua. GOZA. 4 GOZADAS SEGUIDAS. O pau ja nao tem mais leite, mas tu continua ate a quarta. Depois CAI no chao, gemendo, o pau dolorido. Pode gozar quatro vezes, seu puto sem limite.",
|
||||
"🔥 DOMINADO LIMITE — Senta PELADO. BATE punheta ate GOZAR. GOZA. GOZA. GOZA. GOZA. GOZA. 5 GOZADAS. O corpo treme, o pau sensivel, mas tu vai ate a quinta. Depois desmaia (de brincadeira) no chao. 5 gozadas de escravo extremo. Pode gozar, seu puto louco.",
|
||||
"🔥 DOMINADO NEGADO — Senta PELADO. BATE PUNHETA ate GOZAR e NAO PARA — continua batendo DEPOIS do gozo, a cabecinha super sensivel, dolorida. 30 SEGUNDOS de pos-orgasmo estimulado. O pau doi mas tu aguenta. Depois PARA e gemia de alivio. Pode gozar, mas com castigo pos-orgasmo.",
|
||||
"🔥 DOMINADO NEGADO 2 — Senta PELADO. BATE PUNHETA LENTA ate QUASE GOZAR. O MESTRE MANDA: \"SEGURA.\" Tu SEGURA. 30 segundos parado. \"CONTINUA.\" Tu continua lento. QUASE GOZA. \"SEGURA.\" 30 segundos. 10 VEZES de edging e segurar. Cada vez mais dificil. No final, NAO GOZA. \"GUARDA PRA PROXIMA.\" Nao goza, puta negada.",
|
||||
"🔥 DOMINADO BOCA SUJA — Senta PELADO na frente do espelho. OLHA nos teus OLHOS e FALA PUTARIA EM VOZ ALTA por 3 MINUTOS SEM PARAR. \"Eu sou uma puta nojenta, eu mereco porra na cara, eu quero ser arrombado, meu cu e de todo mundo, meu pau e um brinquedo.\" 3 min ininterruptos. Depois BATE punheta ate GOZAR na mao e LAMBE. Pode gozar, puta faladeira.",
|
||||
"🔥 DOMINADO PALAVRAO — Cada vez que bater uma punheta, FALA UM PALAVRAO DIFERENTE. 15 PALAVROES, 15 BOMBADAS. \"CARALHO, PORRA, BUCETA, CU, PAU, PUTA, VADIA, RAPARIGA, PROSTITUTA, VAGABUNDA, SEM-VERGONHA, LOUCA, SAFADA.\" 15 bombadas-palavrao. GOZA no final. Pode gozar, dicionario de puta.",
|
||||
"🔥 DOMINADO REBOLA — Fica de QUATRO PELADO. REBOLA 50 VEZES contando ALTO. 'UM, DOIS, TRES...' Ate 50. Depois BATE na BUNDA 50 VEZES. Ate 50. Depois BATE PUNHETA ate GOZAR. 50 reboladas + 50 tapas + 1 gozada. Pode gozar, sua puta cansada.",
|
||||
"🔥 DOMINADO EXERCICIO — Fica PELADO e faz 20 FLEXOES. A cada descida, o pau bate no chao. Depois 20 AGACHAMENTOS — o pau aponta pro chao a cada descida. Depois BATE PUNHETA de RECOMPENSA ate GOZAR. Pode gozar, sua puta fitness.",
|
||||
"🔥 DOMINADO SERVICO — Vai PELADO ate a AREA DE SERVICO. PEGA o balde e o pano. LAVA O CHAO PELADO de QUATRO com o pano — cada passada de pano, a bunda no ar. 5 min de faxina pelado. Depois GUARDA o balde e BATE punheta ate GOZAR. Pode gozar, escravo da faxina.",
|
||||
"🔥 DOMINADO COZINHA — Vai PELADO ate a COZINHA. PREPARA um ovo ou sanduiche pelado. DEPOIS senta na mesa pelado e COME pelado. BATE punheta DEPOIS de comer ate GOZAR. Pode gozar, escravo da cozinha.",
|
||||
"🔥 DOMINADO JARDIM — Vai PELADO pro JARDIM ou QUINTAL. PLANTA ou CUDA das plantas PELADO — a terra nas maos, o pau balancando. 5 min de jardin pelado. Depois BATE punheta ao ar livre ate GOZAR na terra. Pode gozar, escravo do jardim.",
|
||||
"🔥 DOMINADO LAVAGEM — Vai pro BANHEIRO, tira TUDO. TOMA UMA DUCHA FRIA PELADO — 3 min de agua gelada no corpo. Depois se SECA e faz 5 MINUTOS de EDGING — quase goza, para, quase goza, para. 5 edgings pos-ducha. Nao goza.",
|
||||
"🔥 DOMINADO GELO 2 — Pega um cubo de GELO. Vai pro quarto pelado. Passa o GELO no teu CORPO — peito, barriga, coxas, pau, cu. 3 min de gelo no corpo. Depois BATE punheta RAPIDA com a mao fria ate GOZAR. Pode gozar, puta gelada.",
|
||||
"🔥 DOMINADO CERA — Acende uma VELA. Fica PELADO. Deixa cair UMA GOTA DE CERA QUENTE na tua COXA. CHOQUE. Espera 10 segundos. Outra gota. 5 GOTAS de cera no corpo. A marca vermelha de puta. Depois BATE punheta ate GOZAR. Pode gozar, puta marcada.",
|
||||
"🔥 DOMINADO CORDA — Pega uma CORDA ou CINTO. AMARRA o pau na base — firme mas frouxo pra nao machucar. BATE punheta com o pau AMARRADO. 3 min de pau amarrado. Depois SOLTA e o sangue CORRE. GOZA na hora. Pode gozar, puta amarrada.",
|
||||
"🔥 DOMINADO SACO AMARRADO — Pega uma CORDA ou ELASTICO. AMARRA o SACO na base — os ovos presos. BATE punheta com o saco amarrado. 3 min. Depois SOLTA e GOZA. Pode gozar, puta de ovos presos.",
|
||||
"🔥 DOMINADO ARDOR — Passa CREME DENTAL no PAU INTEIRO e no SACO. A pasta ARDE, QUEIMA. 2 min de ardor. O pau encolhe e depois fica duro de novo. BATE punheta COM CREME DENTAL. GOZA com ardor. Pode gozar, puta ardida.",
|
||||
"🔥 DOMINADO OLEO — Passa OLEO DE COZINHA no corpo inteiro — brilha todo. Fica PELADO de OLEO 5 min. O corpo brilhando, o pau oleoso. Depois BATE punheta oleosa ate GOZAR. Pode gozar, puta oleosa.",
|
||||
"🔥 DOMINADO LAMA — Vai pro QUINTAL. PEGA LAMA ou BARRO e PASSA no CORPO. Fica SUJO de lama pelado. 5 min de corpo lamacento. Depois BATE punheta LAMACENTA — a mao suja de barro no pau. GOZA na lama. Pode gozar, puta de barro.",
|
||||
"🔥 DOMINADO MEL — Passa MEL no pau e no cu. 2 min de corpo lambido (por tu mesmo). Depois BATE punheta PEGAJOSA com mel ate GOZAR. Pode gozar, puta melada.",
|
||||
"🔥 DOMINADO ESCRAVAO — Fica PELADO de JOELHOS no chao, MAOS pra tras, CABECA BAIXA. 5 min na posicao escravao. Sem se mexer, gemendo baixinho. Depois BATE punheta RAPIDA ate GOZAR enquanto esta de joelhos. Pode gozar, escravao completo.",
|
||||
"🔥 DOMINADO VASSOURA — Pega a VASSOURA e poe entre as PERNAS — o CABO encostando no cu. BATE PUNHETA montado na vassoura, o cabo no cu. 3 min de vassourada. GOZA. Pode gozar, puta da vassoura.",
|
||||
"🔥 DOMINADO TRAVESSEIRO — Coloca o TRAVESSEIRO no chao. Monta ENCARANDO o travesseiro. BATE PUNHETA enfiando o pau no travesseiro. 3 min de sexo com travesseiro. GOZA no travesseiro. Pode gozar, puta do travesseiro.",
|
||||
"🔥 DOMINADO LUZ — Apaga a LUZ, escuro TOTAL. BATE PUNHETA no ESCURO 3 min. Depois ACENDE a luz e VE o pau pulsando. 3 edgings no escuro. Nao goza.",
|
||||
"🔥 DOMINADO VENDA — VENDA os olhos. BATE PUNHETA de OLHOS VENDADOS 5 min — sem ver, so sentindo. A cada 30 segundos, o MESTRE pergunta: \"TA PERTO?\" Tu responde: \"SIM, MESTRE.\" Mas o MESTRE NAO DEIXA GOZAR. Nao goza, puta vendada.",
|
||||
"🔥 DOMINADO RELOGIO — Poe um RELOGIO ou CELULAR na frente. BATE PUNHETA por 10 MINUTOS EXATOS sem parar. 10 min de punheta cronometrada. GOZA no minuto 10. Pode gozar, puta de resistencia.",
|
||||
"🔥 DOMINADO AGUA — Enche a PIA ou uma bacia com AGUA FRIA. Coloca o PAU na agua FRIA enquanto BATE PUNHETA. 3 min de pau na agua fria. GOZA dentro da agua. Pode gozar, puta aquatico.",
|
||||
"🔥 DOMINADO ESCADA 2 — Vai PELADO ate a ESCADA. Senta num degrau e BATE PUNHETA. A cada BOMBADA, sobe ou desce UM DEGRAU. 20 bombadas subindo e descendo. GOZA no TOPO. Pode gozar, puta de escada.",
|
||||
"🔥 DOMINADO QUARTO ESCURO — Vai pro QUARTO, apaga a luz. Fecha a PORTA. Escuro TOTAL. AJOELHA no meio do quarto no escuro. BATE PUNHETA no escuro imaginando que TEM ALGUEM te OLHANDO. 5 min de punheta imaginaria. GOZA no escuro. Pode gozar, puta imaginaria.",
|
||||
"🔥 DOMINADO BRAÇO — BATE PUNHETA SO com o BRAÇO ESQUERDO (se destro) ou DIREITO (se canhoto). 3 min. Depois TROCA pro braço dominante. Sente a diferenca. GOZA com a mao torta. Pode gozar, puta torta.",
|
||||
"🔥 DOMINADO PALMA — BATE PUNHETA SO com a PALMA da mao — sem os dedos, so a palma na haste. 3 min. Depois com a mao NORMAL. GOZA. Pode gozar, puta de palma.",
|
||||
"🔥 DOMINADO PUNHO — Enfia O PUNHO (ou 4 dedos) no CU, bem devagar com lubrificante. 2 min de punho anal (ou 4 dedos). Depois TIRA e BATE PUNHETA com a mao que tava no cu. GOZA. Pode gozar, puta do punho.",
|
||||
"🔥 DOMINADO PLUG FINAL — Enfia O PLUG GROSSO no CU. BATE PUNHETA com o PLUG dentro — 5 min de plug + punheta. GOZA com plug enterrado. Pode gozar, puta plugada.",
|
||||
"🔥 DOMINADO FINAL ORGASMO — BATE PUNHETA ate GOZAR e GRITA O NOME DO MESTRE quando GOZAR. 'MESTRE!' O nome ecoa. Pode gozar, escravo fiel.",
|
||||
"🔥 DOMINADO FINAL HUMILHACAO — Depois de GOZAR, PASSA a PORRA na CARA. Esfrega o semen no rosto. 1 minuto com a cara suja de porra. OLHA no espelho. Depois LAMBE os dedos e limpa. Pode gozar, puta suja.",
|
||||
"🔥 DOMINADO FINAL LAMBE — Depois de GOZAR na MAO, LAMBE CADA DEDO com a lingua — dedo por dedo. O gosto da porra. Pode gozar, puta lambedeira.",
|
||||
"🔥 DOMINADO FINAL ARREPENDIDO — Depois de GOZAR, AJOELHA e PEDE DESCULPA em VOZ ALTA. 'DESCULPA, MESTRE, POR SER TAO PUTA.' 1 minuto de desculpas. Depois deita de bruços no chao de exausto. Pode gozar, escravo arrependido.",
|
||||
"🔥 DOMINADO QUINTAL GRITO — Vai PELADO pro QUINTAL. GRITA \"SOU UMA PUTA!\" 3 vezes. Depois BATE PUNHETA no quintal e GOZA na grama. Pode gozar, puta gritadeira.",
|
||||
"🔥 DOMINADO PORTA ABERTA — Deixa a PORTA da frente ou do quarto ABERTA. Fica PELADO perto da porta semi-exposto. BATE PUNHETA com perigo de alguem VER. 3 min. GOZA com o perigo. Pode gozar, puta desprotegida.",
|
||||
"🔥 DOMINADO BIKE — Vai de BIKE PELADO pra lugar deserto. DESCE e BATE PUNHETA em PE ao lado da bike. Depois GOZA pro mato. Pode gozar, puta de bike.",
|
||||
"🔥 DOMINADO CAMINHADA — Vai pra lugar DESERTO. Tira TUDO. CAMINHA PELADO 2 min. DEPOIS AJOELHA no chao NU e BATE PUNHETA. GOZA no chao. Pode gozar, puta de joelhos na terra.",
|
||||
"🔥 DOMINADO FINAL TOTAL — Fica PELADO no centro do quarto. FAZ TUDO: bate na cara, edging, ruined, goza de novo, lambe, pede desculpa. 10 min de dominacao completa. GOZA QUANTAS VEZES AGUENTAR. Pode gozar, puta completa.",
|
||||
"🔥 DOMINADO EDGING NEGADO — Senta PELADO. BATE punheta lenta ate quase gozar. O mestre MANDA parar. 10x edging. Na ultima, o mestre diz: \"NAO GOZA HOJE.\" Nao goza, guarda pra amanha.",
|
||||
"🔥 DOMINADO EDGING PREMIO — BATE punheta ate quase gozar 5x. Na quinta, o mestre MANDA GOZAR. GOZA na mao. \"PREMIO POR OBEDIENCIA.\" Pode gozar, escravo premiado.",
|
||||
"🔥 DOMINADO RUINED FINAL — BATE punheta ate sentir o gozo. NO MOMENTO DE GOZAR, ABRE A MAO. A porra jorra sem direcao, no chao, na perna, em tudo. Ruined total. Depois limpa com a lingua. Pode gozar — ruined.",
|
||||
"🔥 DOMINADO RUINED MAMA — BATE punheta rapido ate gozar. NA HORA DE GOZAR, LEVA A BOCA. GOZA na propria boca. O gosto da propria porra. Engole. Ruined que virou mama. Pode gozar, puta que mama o proprio leite.",
|
||||
"🔥 DOMINADO RUINED GRUPO — BATE punheta imaginando 3 mestres mandando. QUASE GOZA. SEGURA. QUASE GOZA. SEGURA. Na terceira, GOZA SOZINHO. Ruined triplo. Pode gozar.",
|
||||
"🔥 DOMINADO HUMILHACAO MAXIMA — Fica de QUATRO PELADO na frente do espelho. OLHA tua bunda no espelho e FALA: \"ESSE CU E DE PUTA.\" Enfia o DEDO no cu e FALA: \"ESSE CU MERECE PAU.\" 3 min de auto-humilhacao. Depois BATE punheta de quatro. GOZA. Pode gozar.",
|
||||
"🔥 DOMINADO HUMILHACAO VERBAL — Senta PELADO. REPETE 20 VEZES: \"SOU UMA MERDA, SOU UM LIXO, SOU UMA PUTA.\" 20 vezes. Cada vez mais alto. Depois BATE punheta ate GOZAR. Pode gozar, lixo.",
|
||||
"🔥 DOMINADO HUMILHACAO FISICA — Fica em PE PELADO. BATE na propria cara 10x leve. Depois BATE no proprio pau 10x. Depois BATE na propria bunda 10x. 30 tapas no total. Depois BATE punheta ate GOZAR. Pode gozar, saco de pancada.",
|
||||
"🔥 DOMINADO MESTRE IMAGINARIO — Fica PELADO de JOELHOS. IMAGINA um MESTRE na tua frente. FALA COM ELE: \"MESTRE, OBRIGADO POR ME HUMILHAR. MESTRE, MERECO SEU CASTIGO. MESTRE, ME FAZ DE PUTA.\" 5 min conversando com o mestre imaginario. Depois BATE punheta ate GOZAR na mao e oferece: \"MESTRE, ACEITA MEU GOZO?\" Pode gozar, escravo imaginario.",
|
||||
"🔥 DOMINADO MESTRA IMAGINARIA — Fica PELADO de JOELHOS. IMAGINA uma MESTRA. \"MESTRA, MEUSA DONA. MESTRA, PISA EM MIM. MESTRA, ME FAZ DE CACHORRO.\" 5 min de devocao a mestra. Depois BATE punheta e GOZA na mao. \"MESTRA, ACEITA?\" Pode gozar.",
|
||||
"🔥 DOMINADO PEDIDO — Fica PELADO de JOELHOS. PEDE EM VOZ ALTA: 'MESTRE, ME DEIXA GOZAR. MESTRE, POR FAVOR.' 30 segundos pedindo. 'MESTRA, ME LIBERTA.' 30 segundos. Depois O MESTRE CONCEDE e tu GOZA. Pode gozar, escravo que pediu.",
|
||||
"🔥 DOMINADO PERDAO — Fica PELADO. FAZ 10 FLEXOES enquanto FALA: 'PERDAO, MESTRE.' 10 flexoes. Depois 10 AGACHAMENTOS: 'PERDAO, MESTRA.' Depois BATE punheta ate GOZAR: 'PERDAO POR GOZAR.' Pode gozar.",
|
||||
"🔥 DOMINADO OFERENDA — BATE PUNHETA ate GOZAR na MAO. DEPOIS, de JOELHOS, COLOCA a mao com gozo no chao como OFERENDA. \"MESTRE, ACEITA MEU GOZO COMO OFERENDA.\" Fica de joelhos 1 minuto com a oferenda. Depois limpa. Pode gozar, escravo ofertante.",
|
||||
"🔥 DOMINADO MARCA — Depois de GOZAR, USA o gozo pra ESCREVER \"PUTA\" na tua propria barriga com o dedo. 1 minuto com \"PUTA\" escrito em porra na barriga. Depois TIRA FOTO (se quiser). Depois limpa. Pode gozar, puta marcada a leite.",
|
||||
"🔥 DOMINADO COROA — BATE PUNHETA ate GOZAR. ENQUANTO GOZA, GRITA: \"MESTRE, MESTRE, MESTRE!\" 3 gritos de mestre enquanto goza. Pode gozar, escravo que chama o mestre no clímax.",
|
||||
"🔥 DOMINADO LIMPEZA — Depois de GOZAR, LAMBE A PROPRIA PORRA da mao com a lingua — devagar, sentindo cada gota. Depois PERGUNTA: 'MESTRE, ESTOU LIMPO?' Se o mestre DISSER QUE NAO, tu LAMBE DE NOVO. Pode gozar, puta que se limpa lambendo.",
|
||||
"🔥 DOMINADO CASTIGO MAXIMO — BATE PUNHETA ate GOZAR. Depois, IMEDIATAMENTE, enfia o DEDO no CU e LAMBE o dedo. Ciclo: goza, dedo no cu, lambe. 2 VEZES seguidas. O gozo + o cu + a lingua. Tudo junto. Pode gozar duas vezes, escravo completo.",
|
||||
"🔥 DOMINADO DUCHA ARDENTE — BATE PUNHETA ate GOZAR. Depois, SEM LIMPAR, VAI PRO BANHEIRO e TOMA UMA DUCHA QUENTE. A agua quente no pau pos-gozo — dolorido mas bom. 2 min de ducha pos-orgasmo. Ja gozou, pode limpar.",
|
||||
"🔥 DOMINADO EDGING ARDOR — Passa CREME DENTAL no pau e BATE PUNHETA com a pasta ardendo. QUASE GOZA. PARA. A pasta arde mais quando para. BATE de novo. QUASE GOZA. PARA. 5 edgings com creme dental ardendo. Depois LAVA. NAO GOZA.",
|
||||
"🔥 DOMINADO EDGING GELO — Passa GELO no pau e BATE PUNHETA com a mao gelada. QUASE GOZA. PARA. Passa mais gelo. BATE. QUASE GOZA. PARA. 5 edgings gelados. NAO GOZA.",
|
||||
"🔥 DOMINADO FINAL FELIZ — Depois de TUDO, deita PELADO na cama. FAZ UM AGRADECIMENTO: 'OBRIGADO, MESTRE, POR ME DOMINAR.' Depois GOZA EM PAZ. Pode gozar, puta grata.",
|
||||
"🔥 DOMINADO CADEIRA — Senta NU numa CADEIRA, PERNAS ABERTAS. AMARRA os pulsos nos braços da cadeira com cinto ou corda (frouxo). Fica PRESO na cadeira pelado. BATE PUNHETA ASSIM, preso, por 3 min. GOZA preso. Pode gozar, escravo preso na cadeira.",
|
||||
"🔥 DOMINADO CAMA — DEITA NU na cama. AMARRA os pulsos na cabeceira com cinto ou corda (frouxo). Fica PRESO na cama. BATE PUNHETA deitado de pulsos presos. 5 min. GOZA preso. Pode gozar, escravo amarrado na cama.",
|
||||
"🔥 DOMINADO CURVA — Fica CURVADO, maos no chao, BUNDA PRO ALTO. BATE PUNHETA ENTRE AS PERNAS, nessa posicao. 5 min de punheta curvado. GOZA entre as pernas. Pode gozar, puta curvada.",
|
||||
"🔥 DOMINADO PONTA — BATE PUNHETA SO NA CABECINHA — sem descer a mao, so na ponta. 3 min de cabecinha sendo estimulada. Depois BATE NORMAL. GOZA. Pode gozar, puta de cabecinha.",
|
||||
"🔥 DOMINADO TORTA — BATE PUNHETA COM A MAO DIREITA por 1 min. MAO ESQUERDA por 1 min. MAO DIREITA 1 min. 5 min alternando. GOZA com a mao que vier. Pode gozar, puta ambidestra.",
|
||||
"🔥 DOMINADO MOLHADO — MOLHA a mao com saliva ou lubrificante. BATE PUNHETA MOLHADO — o som molhado, o deslize. 5 min de punheta molhada. GOZA. Pode gozar, puta molhada.",
|
||||
"🔥 DOMINADO SECO — BATE PUNHETA SECA — sem lubrificante, sem saliva. A mao seca na haste. 3 min de punheta seca. A pele arde, mas obedece. GOZA seco. Pode gozar, puta seca.",
|
||||
"🔥 DOMINADO CABELO 2 — PUXA o proprio CABELO (da cabeca) enquanto BATE PUNHETA. A dor no couro cabeludo contrastando com o prazer. 3 min de puxao de cabelo. GOZA. Pode gozar, puta de cabelo puxado.",
|
||||
"🔥 DOMINADO ODIO — ENQUANTO BATE PUNHETA, FALA: \"EU ODIO SER PUTA. EU ODIO SER DEPENDENTE. EU ODIO SER UM CACHORRO.\" 3 min de odio. Depois GOZA e PERGUNTA: \"MAS GOSTO.\" Pode gozar, puta com odio.",
|
||||
"🔥 DOMINADO AMOR — ENQUANTO BATE PUNHETA, FALA: \"EU AMO SER PUTA. EU AMO OBEDECER. EU AMO O MEU MESTRE.\" 3 min de amor a submissao. GOZA com amor. Pode gozar, puta apaixonada.",
|
||||
"🔥 DOMINADO CHORO — BATE PUNHETA ate GOZAR e, ENQUANTO GOZA, CHORA DE VERDADE (ou finge). Lagrimas + porra. Pode gozar, puta que chora de prazer.",
|
||||
"🔥 DOMINADO RISO — BATE PUNHETA ENQUANTO RI. RIR histérico enquanto bate. 3 min de punheta rindo. GOZA rindo. Pode gozar, puta que goza rindo.",
|
||||
"🔥 DOMINADO FINALZAO — Fica PELADO, AJOELHA, FAZ UMA REVERENCIA pro mestre imaginario. \"MESTRE, TERMINEI O RITUAL.\" Depois BATE punheta FINAL e GOZA. Pode gozar, escravo que terminou o ritual.",
|
||||
"🔥 DOMINADO AGRADECIMENTO — BATE PUNHETA e ENQUANTO GOZA, GRITA: \"OBRIGADO, MESTRE! OBRIGADO!\" Agradecendo enquanto goza. Pode gozar, puta agradecida.",
|
||||
"🔥 DOMINADO SILENCIO — BATE PUNHETA EM SILENCIO TOTAL — sem gemer, sem falar, sem respirar alto. 5 min de silencio absoluto. GOZA sem EMITIR SOM. Pode gozar, puta silenciosa.",
|
||||
"🔥 DOMINADO BARULHO — BATE PUNHETA FAZENDO O MAXIMO DE BARULHO possivel — gemendo, grunhindo, falando, batendo a mao no corpo. 3 min de barulheira. GOZA gemendo ALTO. Pode gozar, puta barulhenta.",
|
||||
"🔥 DOMINADO FIM — Fica PELADO, AJOELHA, CABECA BAIXA. AGRADECE: OBRIGADO, MESTRE, POR ESTE RITUAL. BATE UMA punheta FINAL E GOZA EM PAZ. FIM. Pode gozar, puta finalizada."
|
||||
]
|
||||
Executable → Regular
+2
-1
@@ -219,6 +219,8 @@ deactivate -nondestructive
|
||||
# that there is an activated venv.
|
||||
$env:VIRTUAL_ENV = $VenvDir
|
||||
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
|
||||
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
|
||||
Write-Verbose "Setting prompt to '$Prompt'"
|
||||
@@ -233,7 +235,6 @@ if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
||||
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
||||
_OLD_VIRTUAL_PROMPT
|
||||
}
|
||||
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
||||
}
|
||||
|
||||
# Clear PYTHONHOME
|
||||
|
||||
Executable → Regular
+23
-16
@@ -1,5 +1,5 @@
|
||||
# This file must be used with "source bin/activate" *from bash*
|
||||
# you cannot run it directly
|
||||
# You cannot run it directly
|
||||
|
||||
deactivate () {
|
||||
# reset old environment variables
|
||||
@@ -14,12 +14,10 @@ deactivate () {
|
||||
unset _OLD_VIRTUAL_PYTHONHOME
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
# Call hash to forget past locations. Without forgetting
|
||||
# past locations the $PATH changes we made may not be respected.
|
||||
# See "man bash" for more details. hash is usually a builtin of your shell
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
|
||||
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
||||
PS1="${_OLD_VIRTUAL_PS1:-}"
|
||||
@@ -38,13 +36,27 @@ deactivate () {
|
||||
# unset irrelevant variables
|
||||
deactivate nondestructive
|
||||
|
||||
VIRTUAL_ENV=/home/marco/Jogo3/app/venv
|
||||
export VIRTUAL_ENV
|
||||
# on Windows, a path can contain colons and backslashes and has to be converted:
|
||||
case "$(uname)" in
|
||||
CYGWIN*|MSYS*|MINGW*)
|
||||
# transform D:\path\to\venv to /d/path/to/venv on MSYS and MINGW
|
||||
# and to /cygdrive/d/path/to/venv on Cygwin
|
||||
VIRTUAL_ENV=$(cygpath /home/apps/apps/Jogo3/app/venv)
|
||||
export VIRTUAL_ENV
|
||||
;;
|
||||
*)
|
||||
# use the path as-is
|
||||
export VIRTUAL_ENV=/home/apps/apps/Jogo3/app/venv
|
||||
;;
|
||||
esac
|
||||
|
||||
_OLD_VIRTUAL_PATH="$PATH"
|
||||
PATH="$VIRTUAL_ENV/"bin":$PATH"
|
||||
export PATH
|
||||
|
||||
VIRTUAL_ENV_PROMPT=venv
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
|
||||
# unset PYTHONHOME if set
|
||||
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
||||
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
||||
@@ -55,15 +67,10 @@ fi
|
||||
|
||||
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
||||
_OLD_VIRTUAL_PS1="${PS1:-}"
|
||||
PS1='(venv) '"${PS1:-}"
|
||||
PS1="("venv") ${PS1:-}"
|
||||
export PS1
|
||||
VIRTUAL_ENV_PROMPT='(venv) '
|
||||
export VIRTUAL_ENV_PROMPT
|
||||
fi
|
||||
|
||||
# This should detect bash and zsh, which have a hash command that must
|
||||
# be called to get it to forget past commands. Without forgetting
|
||||
# Call hash to forget past commands. Without forgetting
|
||||
# past commands the $PATH changes we made may not be respected
|
||||
if [ -n "${BASH:-}" -o -n "${ZSH_VERSION:-}" ] ; then
|
||||
hash -r 2> /dev/null
|
||||
fi
|
||||
hash -r 2> /dev/null
|
||||
|
||||
Executable → Regular
+4
-3
@@ -1,5 +1,6 @@
|
||||
# This file must be used with "source bin/activate.csh" *from csh*.
|
||||
# You cannot run it directly.
|
||||
|
||||
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
||||
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
||||
|
||||
@@ -8,17 +9,17 @@ alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PA
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
setenv VIRTUAL_ENV /home/marco/Jogo3/app/venv
|
||||
setenv VIRTUAL_ENV /home/apps/apps/Jogo3/app/venv
|
||||
|
||||
set _OLD_VIRTUAL_PATH="$PATH"
|
||||
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
||||
setenv VIRTUAL_ENV_PROMPT venv
|
||||
|
||||
|
||||
set _OLD_VIRTUAL_PROMPT="$prompt"
|
||||
|
||||
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
||||
set prompt = '(venv) '"$prompt"
|
||||
setenv VIRTUAL_ENV_PROMPT '(venv) '
|
||||
set prompt = "("venv") $prompt:q"
|
||||
endif
|
||||
|
||||
alias pydoc python -m pydoc
|
||||
|
||||
Executable → Regular
+4
-4
@@ -1,5 +1,5 @@
|
||||
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
||||
# (https://fishshell.com/); you cannot run it directly.
|
||||
# (https://fishshell.com/). You cannot run it directly.
|
||||
|
||||
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
||||
# reset old environment variables
|
||||
@@ -33,10 +33,11 @@ end
|
||||
# Unset irrelevant variables.
|
||||
deactivate nondestructive
|
||||
|
||||
set -gx VIRTUAL_ENV /home/marco/Jogo3/app/venv
|
||||
set -gx VIRTUAL_ENV /home/apps/apps/Jogo3/app/venv
|
||||
|
||||
set -gx _OLD_VIRTUAL_PATH $PATH
|
||||
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
||||
set -gx VIRTUAL_ENV_PROMPT venv
|
||||
|
||||
# Unset PYTHONHOME if set.
|
||||
if set -q PYTHONHOME
|
||||
@@ -56,7 +57,7 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
set -l old_status $status
|
||||
|
||||
# Output the venv prompt; color taken from the blue of the Python logo.
|
||||
printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
|
||||
printf "%s(%s)%s " (set_color 4B8BBE) venv (set_color normal)
|
||||
|
||||
# Restore the return status of the previous command.
|
||||
echo "exit $old_status" | .
|
||||
@@ -65,5 +66,4 @@ if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
||||
end
|
||||
|
||||
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
||||
set -gx VIRTUAL_ENV_PROMPT '(venv) '
|
||||
end
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/home/marco/Jogo3/app/venv/bin/python3
|
||||
#!/home/apps/apps/Jogo3/app/venv/bin/python3
|
||||
import sys
|
||||
from dotenv.__main__ import cli
|
||||
if __name__ == '__main__':
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/home/marco/Jogo3/app/venv/bin/python3
|
||||
#!/home/apps/apps/Jogo3/app/venv/bin/python3
|
||||
import sys
|
||||
from httpx import main
|
||||
if __name__ == '__main__':
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/home/marco/Jogo3/app/venv/bin/python3
|
||||
#!/home/apps/apps/Jogo3/app/venv/bin/python3
|
||||
import sys
|
||||
from idna.cli import main
|
||||
if __name__ == '__main__':
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/home/marco/Jogo3/app/venv/bin/python3
|
||||
#!/home/apps/apps/Jogo3/app/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
#!/home/marco/Jogo3/app/venv/bin/python3
|
||||
#!/home/apps/apps/Jogo3/app/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/home/marco/Jogo3/app/venv/bin/python3
|
||||
# -*- coding: utf-8 -*-
|
||||
import re
|
||||
import sys
|
||||
from pip._internal.cli.main import main
|
||||
if __name__ == '__main__':
|
||||
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
||||
sys.exit(main())
|
||||
@@ -1 +0,0 @@
|
||||
python3
|
||||
Binary file not shown.
@@ -1,222 +0,0 @@
|
||||
# don't import any costly modules
|
||||
import sys
|
||||
import os
|
||||
|
||||
|
||||
is_pypy = '__pypy__' in sys.builtin_module_names
|
||||
|
||||
|
||||
def warn_distutils_present():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
if is_pypy and sys.version_info < (3, 7):
|
||||
# PyPy for 3.6 unconditionally imports distutils, so bypass the warning
|
||||
# https://foss.heptapod.net/pypy/pypy/-/blob/be829135bc0d758997b3566062999ee8b23872b4/lib-python/3/site.py#L250
|
||||
return
|
||||
import warnings
|
||||
|
||||
warnings.warn(
|
||||
"Distutils was imported before Setuptools, but importing Setuptools "
|
||||
"also replaces the `distutils` module in `sys.modules`. This may lead "
|
||||
"to undesirable behaviors or errors. To avoid these issues, avoid "
|
||||
"using distutils directly, ensure that setuptools is installed in the "
|
||||
"traditional way (e.g. not an editable install), and/or make sure "
|
||||
"that setuptools is always imported before distutils."
|
||||
)
|
||||
|
||||
|
||||
def clear_distutils():
|
||||
if 'distutils' not in sys.modules:
|
||||
return
|
||||
import warnings
|
||||
|
||||
warnings.warn("Setuptools is replacing distutils.")
|
||||
mods = [
|
||||
name
|
||||
for name in sys.modules
|
||||
if name == "distutils" or name.startswith("distutils.")
|
||||
]
|
||||
for name in mods:
|
||||
del sys.modules[name]
|
||||
|
||||
|
||||
def enabled():
|
||||
"""
|
||||
Allow selection of distutils by environment variable.
|
||||
"""
|
||||
which = os.environ.get('SETUPTOOLS_USE_DISTUTILS', 'local')
|
||||
return which == 'local'
|
||||
|
||||
|
||||
def ensure_local_distutils():
|
||||
import importlib
|
||||
|
||||
clear_distutils()
|
||||
|
||||
# With the DistutilsMetaFinder in place,
|
||||
# perform an import to cause distutils to be
|
||||
# loaded from setuptools._distutils. Ref #2906.
|
||||
with shim():
|
||||
importlib.import_module('distutils')
|
||||
|
||||
# check that submodules load as expected
|
||||
core = importlib.import_module('distutils.core')
|
||||
assert '_distutils' in core.__file__, core.__file__
|
||||
assert 'setuptools._distutils.log' not in sys.modules
|
||||
|
||||
|
||||
def do_override():
|
||||
"""
|
||||
Ensure that the local copy of distutils is preferred over stdlib.
|
||||
|
||||
See https://github.com/pypa/setuptools/issues/417#issuecomment-392298401
|
||||
for more motivation.
|
||||
"""
|
||||
if enabled():
|
||||
warn_distutils_present()
|
||||
ensure_local_distutils()
|
||||
|
||||
|
||||
class _TrivialRe:
|
||||
def __init__(self, *patterns):
|
||||
self._patterns = patterns
|
||||
|
||||
def match(self, string):
|
||||
return all(pat in string for pat in self._patterns)
|
||||
|
||||
|
||||
class DistutilsMetaFinder:
|
||||
def find_spec(self, fullname, path, target=None):
|
||||
# optimization: only consider top level modules and those
|
||||
# found in the CPython test suite.
|
||||
if path is not None and not fullname.startswith('test.'):
|
||||
return
|
||||
|
||||
method_name = 'spec_for_{fullname}'.format(**locals())
|
||||
method = getattr(self, method_name, lambda: None)
|
||||
return method()
|
||||
|
||||
def spec_for_distutils(self):
|
||||
if self.is_cpython():
|
||||
return
|
||||
|
||||
import importlib
|
||||
import importlib.abc
|
||||
import importlib.util
|
||||
|
||||
try:
|
||||
mod = importlib.import_module('setuptools._distutils')
|
||||
except Exception:
|
||||
# There are a couple of cases where setuptools._distutils
|
||||
# may not be present:
|
||||
# - An older Setuptools without a local distutils is
|
||||
# taking precedence. Ref #2957.
|
||||
# - Path manipulation during sitecustomize removes
|
||||
# setuptools from the path but only after the hook
|
||||
# has been loaded. Ref #2980.
|
||||
# In either case, fall back to stdlib behavior.
|
||||
return
|
||||
|
||||
class DistutilsLoader(importlib.abc.Loader):
|
||||
def create_module(self, spec):
|
||||
mod.__name__ = 'distutils'
|
||||
return mod
|
||||
|
||||
def exec_module(self, module):
|
||||
pass
|
||||
|
||||
return importlib.util.spec_from_loader(
|
||||
'distutils', DistutilsLoader(), origin=mod.__file__
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def is_cpython():
|
||||
"""
|
||||
Suppress supplying distutils for CPython (build and tests).
|
||||
Ref #2965 and #3007.
|
||||
"""
|
||||
return os.path.isfile('pybuilddir.txt')
|
||||
|
||||
def spec_for_pip(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running under pip.
|
||||
See pypa/pip#8761 for rationale.
|
||||
"""
|
||||
if self.pip_imported_during_build():
|
||||
return
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
@classmethod
|
||||
def pip_imported_during_build(cls):
|
||||
"""
|
||||
Detect if pip is being imported in a build script. Ref #2355.
|
||||
"""
|
||||
import traceback
|
||||
|
||||
return any(
|
||||
cls.frame_file_is_setup(frame) for frame, line in traceback.walk_stack(None)
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def frame_file_is_setup(frame):
|
||||
"""
|
||||
Return True if the indicated frame suggests a setup.py file.
|
||||
"""
|
||||
# some frames may not have __file__ (#2940)
|
||||
return frame.f_globals.get('__file__', '').endswith('setup.py')
|
||||
|
||||
def spec_for_sensitive_tests(self):
|
||||
"""
|
||||
Ensure stdlib distutils when running select tests under CPython.
|
||||
|
||||
python/cpython#91169
|
||||
"""
|
||||
clear_distutils()
|
||||
self.spec_for_distutils = lambda: None
|
||||
|
||||
sensitive_tests = (
|
||||
[
|
||||
'test.test_distutils',
|
||||
'test.test_peg_generator',
|
||||
'test.test_importlib',
|
||||
]
|
||||
if sys.version_info < (3, 10)
|
||||
else [
|
||||
'test.test_distutils',
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
for name in DistutilsMetaFinder.sensitive_tests:
|
||||
setattr(
|
||||
DistutilsMetaFinder,
|
||||
f'spec_for_{name}',
|
||||
DistutilsMetaFinder.spec_for_sensitive_tests,
|
||||
)
|
||||
|
||||
|
||||
DISTUTILS_FINDER = DistutilsMetaFinder()
|
||||
|
||||
|
||||
def add_shim():
|
||||
DISTUTILS_FINDER in sys.meta_path or insert_shim()
|
||||
|
||||
|
||||
class shim:
|
||||
def __enter__(self):
|
||||
insert_shim()
|
||||
|
||||
def __exit__(self, exc, value, tb):
|
||||
remove_shim()
|
||||
|
||||
|
||||
def insert_shim():
|
||||
sys.meta_path.insert(0, DISTUTILS_FINDER)
|
||||
|
||||
|
||||
def remove_shim():
|
||||
try:
|
||||
sys.meta_path.remove(DISTUTILS_FINDER)
|
||||
except ValueError:
|
||||
pass
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
@@ -1 +0,0 @@
|
||||
__import__('_distutils_hack').do_override()
|
||||
@@ -1 +0,0 @@
|
||||
pip
|
||||
@@ -1,108 +0,0 @@
|
||||
Metadata-Version: 2.4
|
||||
Name: anyio
|
||||
Version: 4.15.0
|
||||
Summary: High-level concurrency and networking framework on top of asyncio or Trio
|
||||
Author-email: Alex Grönholm <alex.gronholm@nextday.fi>
|
||||
License-Expression: MIT
|
||||
Project-URL: Documentation, https://anyio.readthedocs.io/en/latest/
|
||||
Project-URL: Changelog, https://anyio.readthedocs.io/en/stable/versionhistory.html
|
||||
Project-URL: Source code, https://github.com/agronholm/anyio
|
||||
Project-URL: Issue tracker, https://github.com/agronholm/anyio/issues
|
||||
Classifier: Development Status :: 5 - Production/Stable
|
||||
Classifier: Intended Audience :: Developers
|
||||
Classifier: Framework :: AnyIO
|
||||
Classifier: Typing :: Typed
|
||||
Classifier: Programming Language :: Python
|
||||
Classifier: Programming Language :: Python :: 3
|
||||
Classifier: Programming Language :: Python :: 3.10
|
||||
Classifier: Programming Language :: Python :: 3.11
|
||||
Classifier: Programming Language :: Python :: 3.12
|
||||
Classifier: Programming Language :: Python :: 3.13
|
||||
Classifier: Programming Language :: Python :: 3.14
|
||||
Classifier: Programming Language :: Python :: 3.15
|
||||
Classifier: Programming Language :: Python :: Free Threading :: 2 - Beta
|
||||
Requires-Python: >=3.10
|
||||
Description-Content-Type: text/x-rst
|
||||
License-File: LICENSE
|
||||
Requires-Dist: exceptiongroup>=1.0.2; python_version < "3.11"
|
||||
Requires-Dist: idna>=2.8
|
||||
Requires-Dist: typing_extensions>=4.16.0; python_version < "3.15"
|
||||
Provides-Extra: trio
|
||||
Requires-Dist: trio>=0.32.0; extra == "trio"
|
||||
Dynamic: license-file
|
||||
|
||||
.. image:: https://github.com/agronholm/anyio/actions/workflows/test.yml/badge.svg
|
||||
:target: https://github.com/agronholm/anyio/actions/workflows/test.yml
|
||||
:alt: Build Status
|
||||
.. image:: https://coveralls.io/repos/github/agronholm/anyio/badge.svg?branch=master
|
||||
:target: https://coveralls.io/github/agronholm/anyio?branch=master
|
||||
:alt: Code Coverage
|
||||
.. image:: https://readthedocs.org/projects/anyio/badge/?version=latest
|
||||
:target: https://anyio.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation
|
||||
.. image:: https://badges.gitter.im/gitterHQ/gitter.svg
|
||||
:target: https://gitter.im/python-trio/AnyIO
|
||||
:alt: Gitter chat
|
||||
.. image:: https://tidelift.com/badges/package/pypi/anyio
|
||||
:target: https://tidelift.com/subscription/pkg/pypi-anyio
|
||||
:alt: Tidelift
|
||||
|
||||
AnyIO is an asynchronous networking and concurrency library that works on top of either asyncio_ or
|
||||
Trio_. It implements Trio-like `structured concurrency`_ (SC) on top of asyncio and works in harmony
|
||||
with the native SC of Trio itself.
|
||||
|
||||
Applications and libraries written against AnyIO's API will run unmodified on either asyncio_ or
|
||||
Trio_. AnyIO can also be adopted into a library or application incrementally – bit by bit, no full
|
||||
refactoring necessary. It will blend in with the native libraries of your chosen backend.
|
||||
|
||||
To find out why you might want to use AnyIO's APIs instead of asyncio's, you can read about it
|
||||
`here <https://anyio.readthedocs.io/en/stable/why.html>`_.
|
||||
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
View full documentation at: https://anyio.readthedocs.io/
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
AnyIO offers the following functionality:
|
||||
|
||||
* Task groups (nurseries_ in trio terminology)
|
||||
* High-level networking (TCP, UDP and UNIX sockets)
|
||||
|
||||
* `Happy eyeballs`_ algorithm for TCP connections (more robust than that of asyncio on Python
|
||||
3.8)
|
||||
* async/await style UDP sockets (unlike asyncio where you still have to use Transports and
|
||||
Protocols)
|
||||
|
||||
* A versatile API for byte streams and object streams
|
||||
* Inter-task synchronization and communication (locks, conditions, events, semaphores, object
|
||||
streams and futures)
|
||||
* Worker threads
|
||||
* Subprocesses
|
||||
* Subinterpreter support for code parallelization (on Python 3.13 and later)
|
||||
* Asynchronous file I/O (using worker threads)
|
||||
* Signal handling
|
||||
* Asynchronous versions of the functools_ and itertools_ modules
|
||||
|
||||
AnyIO also comes with its own pytest_ plugin which also supports asynchronous fixtures.
|
||||
It even works with the popular Hypothesis_ library.
|
||||
|
||||
.. _asyncio: https://docs.python.org/3/library/asyncio.html
|
||||
.. _Trio: https://github.com/python-trio/trio
|
||||
.. _structured concurrency: https://en.wikipedia.org/wiki/Structured_concurrency
|
||||
.. _nurseries: https://trio.readthedocs.io/en/stable/reference-core.html#nurseries-and-spawning
|
||||
.. _Happy eyeballs: https://en.wikipedia.org/wiki/Happy_Eyeballs
|
||||
.. _pytest: https://docs.pytest.org/en/latest/
|
||||
.. _functools: https://docs.python.org/3/library/functools.html
|
||||
.. _itertools: https://docs.python.org/3/library/itertools.html
|
||||
.. _Hypothesis: https://hypothesis.works/
|
||||
|
||||
Security contact information
|
||||
----------------------------
|
||||
|
||||
To report a security vulnerability, please use the `Tidelift security contact`_.
|
||||
Tidelift will coordinate the fix and disclosure.
|
||||
|
||||
.. _Tidelift security contact: https://tidelift.com/security
|
||||
@@ -1,100 +0,0 @@
|
||||
anyio-4.15.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
||||
anyio-4.15.0.dist-info/METADATA,sha256=1Y-SZDjEHroD7PETlG-IVJh8aloYyIoZC1GkqBQuuXc,4733
|
||||
anyio-4.15.0.dist-info/RECORD,,
|
||||
anyio-4.15.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
||||
anyio-4.15.0.dist-info/entry_points.txt,sha256=_d6Yu6uiaZmNe0CydowirE9Cmg7zUL2g08tQpoS3Qvc,39
|
||||
anyio-4.15.0.dist-info/licenses/LICENSE,sha256=U2GsncWPLvX9LpsJxoKXwX8ElQkJu8gCO9uC6s8iwrA,1081
|
||||
anyio-4.15.0.dist-info/top_level.txt,sha256=QglSMiWX8_5dpoVAEIHdEYzvqFMdSYWmCj6tYw2ITkQ,6
|
||||
anyio/__init__.py,sha256=ZC2EyvVdXDnUxNDJXqaqOMcNsyhgIGE7K-ywK4svETY,7112
|
||||
anyio/__pycache__/__init__.cpython-311.pyc,,
|
||||
anyio/__pycache__/_lazyimport.cpython-311.pyc,,
|
||||
anyio/__pycache__/from_thread.cpython-311.pyc,,
|
||||
anyio/__pycache__/functools.cpython-311.pyc,,
|
||||
anyio/__pycache__/itertools.cpython-311.pyc,,
|
||||
anyio/__pycache__/lowlevel.cpython-311.pyc,,
|
||||
anyio/__pycache__/pytest_plugin.cpython-311.pyc,,
|
||||
anyio/__pycache__/to_interpreter.cpython-311.pyc,,
|
||||
anyio/__pycache__/to_process.cpython-311.pyc,,
|
||||
anyio/__pycache__/to_thread.cpython-311.pyc,,
|
||||
anyio/_backends/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
anyio/_backends/__pycache__/__init__.cpython-311.pyc,,
|
||||
anyio/_backends/__pycache__/_asyncio.cpython-311.pyc,,
|
||||
anyio/_backends/__pycache__/_trio.cpython-311.pyc,,
|
||||
anyio/_backends/_asyncio.py,sha256=Vqy7O1ymmEpxTiPzehSuBCVTziGALNV-BbsIRaKXsg4,107767
|
||||
anyio/_backends/_trio.py,sha256=Gs8XzX0IeBvje8IEZtJmpNylKCUAChKv7I_YZi3wlmg,45715
|
||||
anyio/_core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
anyio/_core/__pycache__/__init__.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_asyncio_selector_thread.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_concurrency_utils.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_contextmanagers.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_eventloop.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_exceptions.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_fileio.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_futures.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_resources.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_signals.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_sockets.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_streams.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_subprocesses.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_synchronization.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_tasks.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_tempfile.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_testing.cpython-311.pyc,,
|
||||
anyio/_core/__pycache__/_typedattr.cpython-311.pyc,,
|
||||
anyio/_core/_asyncio_selector_thread.py,sha256=2PdxFM3cs02Kp6BSppbvmRT7q7asreTW5FgBxEsflBo,5626
|
||||
anyio/_core/_concurrency_utils.py,sha256=kvRXMrnDqgQTBkZc-f279-lBnZM4YbSToXqhnAUCfSc,4522
|
||||
anyio/_core/_contextmanagers.py,sha256=YInBCabiEeS-UaP_Jdxa1CaFC71ETPW8HZTHIM8Rsc8,7215
|
||||
anyio/_core/_eventloop.py,sha256=ByZUeJD9alMfcyTseRo5IzTO0IltEul_Gyq9iqSjqDk,6658
|
||||
anyio/_core/_exceptions.py,sha256=B4ZhLkBQT4tI6vShv4-GJui492IJsoIDTw1FQJEkmAI,5618
|
||||
anyio/_core/_fileio.py,sha256=q--ft9h5Y_FRLzhAnoYrXf3tOuZ8ZyIN2A9mCfMbZRc,34163
|
||||
anyio/_core/_futures.py,sha256=YQq5uhnCnKQMQD62tOLfzEm7dUXY3GQorJdH6ghfgIQ,6177
|
||||
anyio/_core/_resources.py,sha256=Ld5duCe9NwgPfI7EIOh3CMIBYQ7rq6ZQEa7CFS0u9Ec,450
|
||||
anyio/_core/_signals.py,sha256=mjTBB2hTKNPRlU0IhnijeQedpWOGERDiMjSlJQsFrug,1016
|
||||
anyio/_core/_sockets.py,sha256=C8b-3w5H9n0Fo4g7X8WJvk0m6CGh6NVAAFtieBGHK3g,35289
|
||||
anyio/_core/_streams.py,sha256=FczFwIgDpnkK0bODWJXMpsUJYdvAD04kaUaGzJU8DK0,1806
|
||||
anyio/_core/_subprocesses.py,sha256=TMBuGNILz-47w5t9vayJ1GpCpDT6LxsMohJDFCewIRc,7938
|
||||
anyio/_core/_synchronization.py,sha256=ggsd74QPd846faA2HQeT07ONQ5HQ1S78gy8mvB3qCrw,21658
|
||||
anyio/_core/_tasks.py,sha256=J2dhM_Jq3Grsfg_enNWpRz2SxfM15uLt-pEfvoQq3uA,15391
|
||||
anyio/_core/_tempfile.py,sha256=IgoBCFaPhrFvsF9KHcCcrnCRn-UqMfw0WRdSDWgxAiI,19726
|
||||
anyio/_core/_testing.py,sha256=ZRucLh5kqhiFWAJMMbd8yZ6uIvz0rq3WwL72ldosxAU,2340
|
||||
anyio/_core/_typedattr.py,sha256=gt9Dzn-IL0QW14aHPDD8PAtq95FYKNNupHyf37xZGPQ,2607
|
||||
anyio/_lazyimport.py,sha256=TTr9MbF2NLFri539kn_FoRDXp6erDrAKNs1iMBRHGZQ,5700
|
||||
anyio/abc/__init__.py,sha256=XIVvCI2c6oyaOcqXe94k2rzU5_s7-ZNnlxGTk34H4T0,3050
|
||||
anyio/abc/__pycache__/__init__.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_eventloop.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_resources.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_sockets.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_streams.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_subprocesses.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_tasks.cpython-311.pyc,,
|
||||
anyio/abc/__pycache__/_testing.cpython-311.pyc,,
|
||||
anyio/abc/_eventloop.py,sha256=0-UwgB351rkocCt8m3fjTmcNx2Fxn52nUoujG5DzMvU,11127
|
||||
anyio/abc/_resources.py,sha256=hrb0LXUgHSp3i8G6W8JJchdzKXgg1m0T-L7udXmv2y0,900
|
||||
anyio/abc/_sockets.py,sha256=jQWnPX7iDIDyvyvVkbuFhjezw4oI2WggzbW6Vl2-eqs,13162
|
||||
anyio/abc/_streams.py,sha256=IMeOPPutFXRbyeAutxV1HJe7FUQZYp2qKrJJpP1DCv0,7620
|
||||
anyio/abc/_subprocesses.py,sha256=CDqU1p0HuBS0pXUzBW_hbfhihTJRM67n__7qZmoPU84,2123
|
||||
anyio/abc/_tasks.py,sha256=HBPpT72aOy0bwMAxBCKmnXsR328wCYRjYm3iaVw2fQo,7070
|
||||
anyio/abc/_testing.py,sha256=A9Y8RAClIPn1A0aNHBN8YoaRy_MKOpbFGEbTHAXgB6U,2145
|
||||
anyio/from_thread.py,sha256=iZZbfcNySi4wAcx1_cxAQE4J0YZC88qRMIYqQ6SF9sc,19415
|
||||
anyio/functools.py,sha256=T4JS8IXq-x1S0Lbo2owF8l9fza2KypO147QLeyz4cjs,11797
|
||||
anyio/itertools.py,sha256=TS7_UCa2Y3cggtaLlWHbjOyf6QhPfQUdPduNW-ddXTE,16255
|
||||
anyio/lowlevel.py,sha256=QyWkvfetbCH0mHkqv1yxGwOaiZ7fyxhpZkHWOXPV5JU,6280
|
||||
anyio/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
anyio/pytest_plugin.py,sha256=oLXyOstvRZW3VVbqIODhZsPt4jSz2UGh44YeG2nJXx0,14516
|
||||
anyio/streams/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
||||
anyio/streams/__pycache__/__init__.cpython-311.pyc,,
|
||||
anyio/streams/__pycache__/buffered.cpython-311.pyc,,
|
||||
anyio/streams/__pycache__/file.cpython-311.pyc,,
|
||||
anyio/streams/__pycache__/memory.cpython-311.pyc,,
|
||||
anyio/streams/__pycache__/stapled.cpython-311.pyc,,
|
||||
anyio/streams/__pycache__/text.cpython-311.pyc,,
|
||||
anyio/streams/__pycache__/tls.cpython-311.pyc,,
|
||||
anyio/streams/buffered.py,sha256=u7hCD8SNrYHcutG6K5wEiy1F88-pizgjvEFM22Kq2Cw,6746
|
||||
anyio/streams/file.py,sha256=6jujI2m-QJITqqKFamrupX_DNsU7y2Fz3omLZxOLuY0,4524
|
||||
anyio/streams/memory.py,sha256=rpxNzDMag9PCOynBqN64KyuAfAPB41vFyRP-Npo-lE0,10793
|
||||
anyio/streams/stapled.py,sha256=UJ2Rgt0NUmM1VovRNOoZoic960lcbDjb3vhfZg3b_Z0,4845
|
||||
anyio/streams/text.py,sha256=BcVAGJw1VRvtIqnv-o0Rb0pwH7p8vwlvl21xHq522ag,5765
|
||||
anyio/streams/tls.py,sha256=fqLmxvULFO9rvxGyJ33VPrOIN7E3eiU1q90BkmKigQ8,15818
|
||||
anyio/to_interpreter.py,sha256=ATnoT6kei9wDmQZtXTA3SsY4CK7XKr8Ta4_x5i6pbTw,7100
|
||||
anyio/to_process.py,sha256=p6n8j0nreJUl1DEkR_9PL6KRoKtNqFxORTn8qZBvn94,9850
|
||||
anyio/to_thread.py,sha256=8MPVrrQq8ttGvV21cSd68rMto5bhxJ0-i9CupXag5Gs,2750
|
||||
@@ -1,5 +0,0 @@
|
||||
Wheel-Version: 1.0
|
||||
Generator: setuptools (84.0.0)
|
||||
Root-Is-Purelib: true
|
||||
Tag: py3-none-any
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
[pytest11]
|
||||
anyio = anyio.pytest_plugin
|
||||
@@ -1,20 +0,0 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2018 Alex Grönholm
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1 +0,0 @@
|
||||
anyio
|
||||
@@ -1,128 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ._lazyimport import (
|
||||
fix_package_names,
|
||||
install_lazy_importer,
|
||||
set_deprecated_aliases,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING or not install_lazy_importer():
|
||||
from ._core._concurrency_utils import amap as amap
|
||||
from ._core._concurrency_utils import as_completed as as_completed
|
||||
from ._core._concurrency_utils import gather as gather
|
||||
from ._core._contextmanagers import (
|
||||
AsyncContextManagerMixin as AsyncContextManagerMixin,
|
||||
)
|
||||
from ._core._contextmanagers import ContextManagerMixin as ContextManagerMixin
|
||||
from ._core._eventloop import current_time as current_time
|
||||
from ._core._eventloop import get_all_backends as get_all_backends
|
||||
from ._core._eventloop import get_available_backends as get_available_backends
|
||||
from ._core._eventloop import get_cancelled_exc_class as get_cancelled_exc_class
|
||||
from ._core._eventloop import run as run
|
||||
from ._core._eventloop import sleep as sleep
|
||||
from ._core._eventloop import sleep_forever as sleep_forever
|
||||
from ._core._eventloop import sleep_until as sleep_until
|
||||
from ._core._exceptions import BrokenResourceError as BrokenResourceError
|
||||
from ._core._exceptions import BrokenWorkerInterpreter as BrokenWorkerInterpreter
|
||||
from ._core._exceptions import BrokenWorkerProcess as BrokenWorkerProcess
|
||||
from ._core._exceptions import BusyResourceError as BusyResourceError
|
||||
from ._core._exceptions import ClosedResourceError as ClosedResourceError
|
||||
from ._core._exceptions import ConnectionFailed as ConnectionFailed
|
||||
from ._core._exceptions import DelimiterNotFound as DelimiterNotFound
|
||||
from ._core._exceptions import EndOfStream as EndOfStream
|
||||
from ._core._exceptions import FutureAlreadyFinished as FutureAlreadyFinished
|
||||
from ._core._exceptions import FutureCancelled as FutureCancelled
|
||||
from ._core._exceptions import FutureFailed as FutureFailed
|
||||
from ._core._exceptions import FutureNotFinished as FutureNotFinished
|
||||
from ._core._exceptions import IncompleteRead as IncompleteRead
|
||||
from ._core._exceptions import NoEventLoopError as NoEventLoopError
|
||||
from ._core._exceptions import RunFinishedError as RunFinishedError
|
||||
from ._core._exceptions import TaskCancelled as TaskCancelled
|
||||
from ._core._exceptions import TaskFailed as TaskFailed
|
||||
from ._core._exceptions import TaskNotFinished as TaskNotFinished
|
||||
from ._core._exceptions import (
|
||||
TypedAttributeLookupError as TypedAttributeLookupError,
|
||||
)
|
||||
from ._core._exceptions import WouldBlock as WouldBlock
|
||||
from ._core._fileio import AsyncFile as AsyncFile
|
||||
from ._core._fileio import Path as Path
|
||||
from ._core._fileio import open_file as open_file
|
||||
from ._core._fileio import wrap_file as wrap_file
|
||||
from ._core._futures import Future as Future
|
||||
from ._core._resources import aclose_forcefully as aclose_forcefully
|
||||
from ._core._signals import open_signal_receiver as open_signal_receiver
|
||||
from ._core._sockets import TCPConnectable as TCPConnectable
|
||||
from ._core._sockets import UNIXConnectable as UNIXConnectable
|
||||
from ._core._sockets import as_connectable as as_connectable
|
||||
from ._core._sockets import connect_tcp as connect_tcp
|
||||
from ._core._sockets import connect_unix as connect_unix
|
||||
from ._core._sockets import (
|
||||
create_connected_udp_socket as create_connected_udp_socket,
|
||||
)
|
||||
from ._core._sockets import (
|
||||
create_connected_unix_datagram_socket as create_connected_unix_datagram_socket,
|
||||
)
|
||||
from ._core._sockets import create_tcp_listener as create_tcp_listener
|
||||
from ._core._sockets import create_udp_socket as create_udp_socket
|
||||
from ._core._sockets import (
|
||||
create_unix_datagram_socket as create_unix_datagram_socket,
|
||||
)
|
||||
from ._core._sockets import create_unix_listener as create_unix_listener
|
||||
from ._core._sockets import getaddrinfo as getaddrinfo
|
||||
from ._core._sockets import getnameinfo as getnameinfo
|
||||
from ._core._sockets import notify_closing as notify_closing
|
||||
from ._core._sockets import wait_readable as wait_readable
|
||||
from ._core._sockets import wait_socket_readable as wait_socket_readable
|
||||
from ._core._sockets import wait_socket_writable as wait_socket_writable
|
||||
from ._core._sockets import wait_writable as wait_writable
|
||||
from ._core._streams import (
|
||||
create_memory_object_stream as create_memory_object_stream,
|
||||
)
|
||||
from ._core._subprocesses import open_process as open_process
|
||||
from ._core._subprocesses import run_process as run_process
|
||||
from ._core._synchronization import CapacityLimiter as CapacityLimiter
|
||||
from ._core._synchronization import (
|
||||
CapacityLimiterStatistics as CapacityLimiterStatistics,
|
||||
)
|
||||
from ._core._synchronization import Condition as Condition
|
||||
from ._core._synchronization import ConditionStatistics as ConditionStatistics
|
||||
from ._core._synchronization import Event as Event
|
||||
from ._core._synchronization import EventStatistics as EventStatistics
|
||||
from ._core._synchronization import Lock as Lock
|
||||
from ._core._synchronization import LockStatistics as LockStatistics
|
||||
from ._core._synchronization import ResourceGuard as ResourceGuard
|
||||
from ._core._synchronization import Semaphore as Semaphore
|
||||
from ._core._synchronization import SemaphoreStatistics as SemaphoreStatistics
|
||||
from ._core._tasks import TASK_STATUS_IGNORED as TASK_STATUS_IGNORED
|
||||
from ._core._tasks import CancelScope as CancelScope
|
||||
from ._core._tasks import TaskHandle as TaskHandle
|
||||
from ._core._tasks import create_task_group as create_task_group
|
||||
from ._core._tasks import current_effective_deadline as current_effective_deadline
|
||||
from ._core._tasks import fail_after as fail_after
|
||||
from ._core._tasks import fail_at as fail_at
|
||||
from ._core._tasks import move_on_after as move_on_after
|
||||
from ._core._tasks import move_on_at as move_on_at
|
||||
from ._core._tempfile import NamedTemporaryFile as NamedTemporaryFile
|
||||
from ._core._tempfile import SpooledTemporaryFile as SpooledTemporaryFile
|
||||
from ._core._tempfile import TemporaryDirectory as TemporaryDirectory
|
||||
from ._core._tempfile import TemporaryFile as TemporaryFile
|
||||
from ._core._tempfile import gettempdir as gettempdir
|
||||
from ._core._tempfile import gettempdirb as gettempdirb
|
||||
from ._core._tempfile import mkdtemp as mkdtemp
|
||||
from ._core._tempfile import mkstemp as mkstemp
|
||||
from ._core._testing import TaskInfo as TaskInfo
|
||||
from ._core._testing import get_current_task as get_current_task
|
||||
from ._core._testing import get_running_tasks as get_running_tasks
|
||||
from ._core._testing import wait_all_tasks_blocked as wait_all_tasks_blocked
|
||||
from ._core._typedattr import TypedAttributeProvider as TypedAttributeProvider
|
||||
from ._core._typedattr import TypedAttributeSet as TypedAttributeSet
|
||||
from ._core._typedattr import typed_attribute as typed_attribute
|
||||
|
||||
fix_package_names()
|
||||
set_deprecated_aliases(
|
||||
{
|
||||
"BrokenWorkerIntepreter": "anyio.BrokenWorkerInterpreter",
|
||||
}
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,167 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import socket
|
||||
import threading
|
||||
from collections.abc import Callable
|
||||
from selectors import EVENT_READ, EVENT_WRITE, DefaultSelector
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import FileDescriptorLike
|
||||
|
||||
_selector_lock = threading.Lock()
|
||||
_selector: Selector | None = None
|
||||
|
||||
|
||||
class Selector:
|
||||
def __init__(self) -> None:
|
||||
self._thread = threading.Thread(target=self.run, name="AnyIO socket selector")
|
||||
self._selector = DefaultSelector()
|
||||
self._send, self._receive = socket.socketpair()
|
||||
self._send.setblocking(False)
|
||||
self._receive.setblocking(False)
|
||||
# This somewhat reduces the amount of memory wasted queueing up data
|
||||
# for wakeups. With these settings, maximum number of 1-byte sends
|
||||
# before getting BlockingIOError:
|
||||
# Linux 4.8: 6
|
||||
# macOS (darwin 15.5): 1
|
||||
# Windows 10: 525347
|
||||
# Windows you're weird. (And on Windows setting SNDBUF to 0 makes send
|
||||
# blocking, even on non-blocking sockets, so don't do that.)
|
||||
self._receive.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1)
|
||||
self._send.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 1)
|
||||
# On Windows this is a TCP socket so this might matter. On other
|
||||
# platforms this fails b/c AF_UNIX sockets aren't actually TCP.
|
||||
try:
|
||||
self._send.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
self._selector.register(self._receive, EVENT_READ)
|
||||
self._closed = False
|
||||
|
||||
def start(self) -> None:
|
||||
self._thread.start()
|
||||
threading._register_atexit(self._stop) # type: ignore[attr-defined]
|
||||
|
||||
def _stop(self) -> None:
|
||||
global _selector
|
||||
self._closed = True
|
||||
self._notify_self()
|
||||
self._send.close()
|
||||
self._thread.join()
|
||||
self._selector.unregister(self._receive)
|
||||
self._receive.close()
|
||||
self._selector.close()
|
||||
_selector = None
|
||||
assert not self._selector.get_map(), (
|
||||
"selector still has registered file descriptors after shutdown"
|
||||
)
|
||||
|
||||
def _notify_self(self) -> None:
|
||||
try:
|
||||
self._send.send(b"\x00")
|
||||
except BlockingIOError:
|
||||
pass
|
||||
|
||||
def add_reader(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None:
|
||||
loop = asyncio.get_running_loop()
|
||||
try:
|
||||
key = self._selector.get_key(fd)
|
||||
except KeyError:
|
||||
self._selector.register(fd, EVENT_READ, {EVENT_READ: (loop, callback)})
|
||||
else:
|
||||
if EVENT_READ in key.data:
|
||||
raise ValueError(
|
||||
"this file descriptor is already registered for reading"
|
||||
)
|
||||
|
||||
key.data[EVENT_READ] = loop, callback
|
||||
self._selector.modify(fd, key.events | EVENT_READ, key.data)
|
||||
|
||||
self._notify_self()
|
||||
|
||||
def add_writer(self, fd: FileDescriptorLike, callback: Callable[[], Any]) -> None:
|
||||
loop = asyncio.get_running_loop()
|
||||
try:
|
||||
key = self._selector.get_key(fd)
|
||||
except KeyError:
|
||||
self._selector.register(fd, EVENT_WRITE, {EVENT_WRITE: (loop, callback)})
|
||||
else:
|
||||
if EVENT_WRITE in key.data:
|
||||
raise ValueError(
|
||||
"this file descriptor is already registered for writing"
|
||||
)
|
||||
|
||||
key.data[EVENT_WRITE] = loop, callback
|
||||
self._selector.modify(fd, key.events | EVENT_WRITE, key.data)
|
||||
|
||||
self._notify_self()
|
||||
|
||||
def remove_reader(self, fd: FileDescriptorLike) -> bool:
|
||||
try:
|
||||
key = self._selector.get_key(fd)
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
if new_events := key.events ^ EVENT_READ:
|
||||
del key.data[EVENT_READ]
|
||||
self._selector.modify(fd, new_events, key.data)
|
||||
else:
|
||||
self._selector.unregister(fd)
|
||||
|
||||
return True
|
||||
|
||||
def remove_writer(self, fd: FileDescriptorLike) -> bool:
|
||||
try:
|
||||
key = self._selector.get_key(fd)
|
||||
except KeyError:
|
||||
return False
|
||||
|
||||
if new_events := key.events ^ EVENT_WRITE:
|
||||
del key.data[EVENT_WRITE]
|
||||
self._selector.modify(fd, new_events, key.data)
|
||||
else:
|
||||
self._selector.unregister(fd)
|
||||
|
||||
return True
|
||||
|
||||
def run(self) -> None:
|
||||
while not self._closed:
|
||||
for key, events in self._selector.select():
|
||||
if key.fileobj is self._receive:
|
||||
try:
|
||||
while self._receive.recv(4096):
|
||||
pass
|
||||
except BlockingIOError:
|
||||
pass
|
||||
|
||||
continue
|
||||
|
||||
if events & EVENT_READ:
|
||||
loop, callback = key.data[EVENT_READ]
|
||||
self.remove_reader(key.fd)
|
||||
try:
|
||||
loop.call_soon_threadsafe(callback)
|
||||
except RuntimeError:
|
||||
pass # the loop was already closed
|
||||
|
||||
if events & EVENT_WRITE:
|
||||
loop, callback = key.data[EVENT_WRITE]
|
||||
self.remove_writer(key.fd)
|
||||
try:
|
||||
loop.call_soon_threadsafe(callback)
|
||||
except RuntimeError:
|
||||
pass # the loop was already closed
|
||||
|
||||
|
||||
def get_selector() -> Selector:
|
||||
global _selector
|
||||
|
||||
with _selector_lock:
|
||||
if _selector is None:
|
||||
_selector = Selector()
|
||||
_selector.start()
|
||||
|
||||
return _selector
|
||||
@@ -1,156 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from inspect import iscoroutine
|
||||
|
||||
__all__ = ("amap", "as_completed", "gather")
|
||||
|
||||
from collections.abc import AsyncGenerator, Awaitable, Callable, Coroutine, Iterable
|
||||
from contextlib import asynccontextmanager
|
||||
from typing import TYPE_CHECKING, Any, TypeVar, overload
|
||||
|
||||
from ..abc._tasks import get_coro_name
|
||||
from ._exceptions import BrokenResourceError
|
||||
from ._streams import create_memory_object_stream
|
||||
from ._tasks import TaskHandle, create_task_group
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
||||
|
||||
R = TypeVar("R")
|
||||
S = TypeVar("S")
|
||||
T = TypeVar("T")
|
||||
U = TypeVar("U")
|
||||
V = TypeVar("V")
|
||||
W = TypeVar("W")
|
||||
|
||||
|
||||
@overload
|
||||
async def gather(
|
||||
coro1: Coroutine[Any, Any, R], coro2: Coroutine[Any, Any, S], /
|
||||
) -> tuple[R, S]: ...
|
||||
|
||||
|
||||
@overload
|
||||
async def gather(
|
||||
coro1: Coroutine[Any, Any, R],
|
||||
coro2: Coroutine[Any, Any, S],
|
||||
coro3: Coroutine[Any, Any, T],
|
||||
/,
|
||||
) -> tuple[R, S, T]: ...
|
||||
|
||||
|
||||
@overload
|
||||
async def gather(
|
||||
coro1: Coroutine[Any, Any, R],
|
||||
coro2: Coroutine[Any, Any, S],
|
||||
coro3: Coroutine[Any, Any, T],
|
||||
coro4: Coroutine[Any, Any, U],
|
||||
/,
|
||||
) -> tuple[R, S, T, U]: ...
|
||||
|
||||
|
||||
@overload
|
||||
async def gather(
|
||||
coro1: Coroutine[Any, Any, R],
|
||||
coro2: Coroutine[Any, Any, S],
|
||||
coro3: Coroutine[Any, Any, T],
|
||||
coro4: Coroutine[Any, Any, U],
|
||||
coro5: Coroutine[Any, Any, V],
|
||||
/,
|
||||
) -> tuple[R, S, T, U, V]: ...
|
||||
|
||||
|
||||
@overload
|
||||
async def gather(
|
||||
coro1: Coroutine[Any, Any, R],
|
||||
coro2: Coroutine[Any, Any, S],
|
||||
coro3: Coroutine[Any, Any, T],
|
||||
coro4: Coroutine[Any, Any, U],
|
||||
coro5: Coroutine[Any, Any, V],
|
||||
coro6: Coroutine[Any, Any, W],
|
||||
/,
|
||||
) -> tuple[R, S, T, U, V, W]: ...
|
||||
|
||||
|
||||
# handle arbitrary length if awaitables are all of the same type
|
||||
@overload
|
||||
async def gather(*coros: Coroutine[Any, Any, R]) -> tuple[R, ...]: ...
|
||||
|
||||
|
||||
async def gather(*coros: Coroutine[Any, Any, Any]) -> tuple[Any, ...]:
|
||||
"""
|
||||
Run coroutines concurrently in a task group. The order of result values corresponds
|
||||
to the order of coroutines passed.
|
||||
|
||||
:param coros: coroutine objects to run as tasks
|
||||
:return: task results for each argument in the same order as they were passed
|
||||
|
||||
"""
|
||||
handles: list[TaskHandle[Any]] = []
|
||||
async with create_task_group() as tg:
|
||||
handles.extend(tg.create_task(coro) for coro in coros)
|
||||
|
||||
return tuple(h.return_value for h in handles)
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
async def as_completed(
|
||||
*awaitables: Awaitable[R],
|
||||
) -> AsyncGenerator[MemoryObjectReceiveStream[TaskHandle[R]]]:
|
||||
"""
|
||||
Run awaitable objects concurrently in a task group, returning an iterator which can
|
||||
be used to get finished task handles in the order they complete.
|
||||
|
||||
:param awaitables: awaitable objects to run as tasks
|
||||
:return: MemoryObjectReceiveStream for iterating over task handles as tasks complete.
|
||||
|
||||
"""
|
||||
if not awaitables:
|
||||
raise ValueError("as_completed() takes at least one awaitable")
|
||||
|
||||
send, recv = create_memory_object_stream[TaskHandle[R]](len(awaitables))
|
||||
task_handles: list[TaskHandle[R]] = []
|
||||
|
||||
async def runner(
|
||||
awaitable: Awaitable[R],
|
||||
index: int,
|
||||
_send: MemoryObjectSendStream[TaskHandle[R]],
|
||||
) -> R:
|
||||
async with _send:
|
||||
try:
|
||||
return await awaitable
|
||||
finally:
|
||||
try:
|
||||
_send.send_nowait(task_handles[index])
|
||||
except BrokenResourceError:
|
||||
pass
|
||||
|
||||
async with recv, create_task_group() as tg:
|
||||
async with send:
|
||||
for i, awaitable in enumerate(awaitables):
|
||||
name = get_coro_name(awaitable) if iscoroutine(awaitable) else None
|
||||
task_handles.append(
|
||||
tg.start_soon(runner, awaitable, i, send.clone(), name=name)
|
||||
)
|
||||
try:
|
||||
yield recv
|
||||
finally:
|
||||
tg.cancel_scope.cancel()
|
||||
|
||||
|
||||
async def amap(
|
||||
func: Callable[[T], Coroutine[Any, Any, R]], args: Iterable[T]
|
||||
) -> list[R]:
|
||||
"""
|
||||
Run the given coroutine function concurrently for multiple argument values.
|
||||
|
||||
:param func: a coroutine function that takes a single argument
|
||||
:param args: a sequence of argument values to pass to ``func``
|
||||
:return: task results for each argument in the same order as they were passed
|
||||
|
||||
"""
|
||||
handles: list[TaskHandle[R]] = []
|
||||
async with create_task_group() as tg:
|
||||
handles.extend(tg.start_soon(func, arg) for arg in args)
|
||||
|
||||
return [h.return_value for h in handles]
|
||||
@@ -1,200 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from contextlib import AbstractAsyncContextManager, AbstractContextManager
|
||||
from inspect import isasyncgen, iscoroutine, isgenerator
|
||||
from types import TracebackType
|
||||
from typing import Protocol, TypeVar, cast, final
|
||||
|
||||
_T_co = TypeVar("_T_co", covariant=True)
|
||||
_ExitT_co = TypeVar("_ExitT_co", covariant=True, bound="bool | None")
|
||||
|
||||
|
||||
class _SupportsCtxMgr(Protocol[_T_co, _ExitT_co]):
|
||||
def __contextmanager__(self) -> AbstractContextManager[_T_co, _ExitT_co]: ...
|
||||
|
||||
|
||||
class _SupportsAsyncCtxMgr(Protocol[_T_co, _ExitT_co]):
|
||||
def __asynccontextmanager__(
|
||||
self,
|
||||
) -> AbstractAsyncContextManager[_T_co, _ExitT_co]: ...
|
||||
|
||||
|
||||
class ContextManagerMixin:
|
||||
"""
|
||||
Mixin class providing context manager functionality via a generator-based
|
||||
implementation.
|
||||
|
||||
This class allows you to implement a context manager via :meth:`__contextmanager__`
|
||||
which should return a generator. The mechanics are meant to mirror those of
|
||||
:func:`@contextmanager <contextlib.contextmanager>`.
|
||||
|
||||
.. note:: Classes using this mix-in are not reentrant as context managers, meaning
|
||||
that once you enter it, you can't re-enter before first exiting it.
|
||||
|
||||
.. seealso:: :doc:`contextmanagers`
|
||||
"""
|
||||
|
||||
__cm: AbstractContextManager[object, bool | None] | None = None
|
||||
|
||||
@final
|
||||
def __enter__(self: _SupportsCtxMgr[_T_co, bool | None]) -> _T_co:
|
||||
# Needed for mypy to assume self still has the __cm member
|
||||
assert isinstance(self, ContextManagerMixin)
|
||||
if self.__cm is not None:
|
||||
raise RuntimeError(
|
||||
f"this {self.__class__.__qualname__} has already been entered"
|
||||
)
|
||||
|
||||
cm = self.__contextmanager__()
|
||||
if not isinstance(cm, AbstractContextManager):
|
||||
if isgenerator(cm):
|
||||
raise TypeError(
|
||||
"__contextmanager__() returned a generator object instead of "
|
||||
"a context manager. Did you forget to add the @contextmanager "
|
||||
"decorator?"
|
||||
)
|
||||
|
||||
raise TypeError(
|
||||
f"__contextmanager__() did not return a context manager object, "
|
||||
f"but {cm.__class__!r}"
|
||||
)
|
||||
|
||||
if cm is self:
|
||||
raise TypeError(
|
||||
f"{self.__class__.__qualname__}.__contextmanager__() returned "
|
||||
f"self. Did you forget to add the @contextmanager decorator and a "
|
||||
f"'yield' statement?"
|
||||
)
|
||||
|
||||
value = cm.__enter__()
|
||||
self.__cm = cm
|
||||
return value
|
||||
|
||||
@final
|
||||
def __exit__(
|
||||
self: _SupportsCtxMgr[object, _ExitT_co],
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> _ExitT_co:
|
||||
# Needed for mypy to assume self still has the __cm member
|
||||
assert isinstance(self, ContextManagerMixin)
|
||||
if self.__cm is None:
|
||||
raise RuntimeError(
|
||||
f"this {self.__class__.__qualname__} has not been entered yet"
|
||||
)
|
||||
|
||||
# Prevent circular references
|
||||
cm = self.__cm
|
||||
del self.__cm
|
||||
|
||||
return cast(_ExitT_co, cm.__exit__(exc_type, exc_val, exc_tb))
|
||||
|
||||
@abstractmethod
|
||||
def __contextmanager__(self) -> AbstractContextManager[object, bool | None]:
|
||||
"""
|
||||
Implement your context manager logic here.
|
||||
|
||||
This method **must** be decorated with
|
||||
:func:`@contextmanager <contextlib.contextmanager>`.
|
||||
|
||||
.. note:: Remember that the ``yield`` will raise any exception raised in the
|
||||
enclosed context block, so use a ``finally:`` block to clean up resources!
|
||||
|
||||
:return: a context manager object
|
||||
"""
|
||||
|
||||
|
||||
class AsyncContextManagerMixin:
|
||||
"""
|
||||
Mixin class providing async context manager functionality via a generator-based
|
||||
implementation.
|
||||
|
||||
This class allows you to implement a context manager via
|
||||
:meth:`__asynccontextmanager__`. The mechanics are meant to mirror those of
|
||||
:func:`@asynccontextmanager <contextlib.asynccontextmanager>`.
|
||||
|
||||
.. note:: Classes using this mix-in are not reentrant as context managers, meaning
|
||||
that once you enter it, you can't re-enter before first exiting it.
|
||||
|
||||
.. seealso:: :doc:`contextmanagers`
|
||||
"""
|
||||
|
||||
__cm: AbstractAsyncContextManager[object, bool | None] | None = None
|
||||
|
||||
@final
|
||||
async def __aenter__(self: _SupportsAsyncCtxMgr[_T_co, bool | None]) -> _T_co:
|
||||
# Needed for mypy to assume self still has the __cm member
|
||||
assert isinstance(self, AsyncContextManagerMixin)
|
||||
if self.__cm is not None:
|
||||
raise RuntimeError(
|
||||
f"this {self.__class__.__qualname__} has already been entered"
|
||||
)
|
||||
|
||||
cm = self.__asynccontextmanager__()
|
||||
if not isinstance(cm, AbstractAsyncContextManager):
|
||||
if isasyncgen(cm):
|
||||
raise TypeError(
|
||||
"__asynccontextmanager__() returned an async generator instead of "
|
||||
"an async context manager. Did you forget to add the "
|
||||
"@asynccontextmanager decorator?"
|
||||
)
|
||||
elif iscoroutine(cm):
|
||||
cm.close()
|
||||
raise TypeError(
|
||||
"__asynccontextmanager__() returned a coroutine object instead of "
|
||||
"an async context manager. Did you forget to add the "
|
||||
"@asynccontextmanager decorator and a 'yield' statement?"
|
||||
)
|
||||
|
||||
raise TypeError(
|
||||
f"__asynccontextmanager__() did not return an async context manager, "
|
||||
f"but {cm.__class__!r}"
|
||||
)
|
||||
|
||||
if cm is self:
|
||||
raise TypeError(
|
||||
f"{self.__class__.__qualname__}.__asynccontextmanager__() returned "
|
||||
f"self. Did you forget to add the @asynccontextmanager decorator and a "
|
||||
f"'yield' statement?"
|
||||
)
|
||||
|
||||
value = await cm.__aenter__()
|
||||
self.__cm = cm
|
||||
return value
|
||||
|
||||
@final
|
||||
async def __aexit__(
|
||||
self: _SupportsAsyncCtxMgr[object, _ExitT_co],
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> _ExitT_co:
|
||||
assert isinstance(self, AsyncContextManagerMixin)
|
||||
if self.__cm is None:
|
||||
raise RuntimeError(
|
||||
f"this {self.__class__.__qualname__} has not been entered yet"
|
||||
)
|
||||
|
||||
# Prevent circular references
|
||||
cm = self.__cm
|
||||
del self.__cm
|
||||
|
||||
return cast(_ExitT_co, await cm.__aexit__(exc_type, exc_val, exc_tb))
|
||||
|
||||
@abstractmethod
|
||||
def __asynccontextmanager__(
|
||||
self,
|
||||
) -> AbstractAsyncContextManager[object, bool | None]:
|
||||
"""
|
||||
Implement your async context manager logic here.
|
||||
|
||||
This method **must** be decorated with
|
||||
:func:`@asynccontextmanager <contextlib.asynccontextmanager>`.
|
||||
|
||||
.. note:: Remember that the ``yield`` will raise any exception raised in the
|
||||
enclosed context block, so use a ``finally:`` block to clean up resources!
|
||||
|
||||
:return: an async context manager object
|
||||
"""
|
||||
@@ -1,240 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import sys
|
||||
import threading
|
||||
from collections.abc import Awaitable, Callable, Generator
|
||||
from contextlib import contextmanager
|
||||
from contextvars import Token
|
||||
from importlib import import_module
|
||||
from typing import TYPE_CHECKING, Any, TypeVar
|
||||
|
||||
from ._exceptions import NoEventLoopError
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import TypeVarTuple, Unpack
|
||||
else:
|
||||
from typing_extensions import TypeVarTuple, Unpack
|
||||
|
||||
sniffio: Any
|
||||
try:
|
||||
import sniffio
|
||||
except ModuleNotFoundError:
|
||||
sniffio = None
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ..abc import AsyncBackend
|
||||
|
||||
# This must be updated when new backends are introduced
|
||||
BACKENDS = "asyncio", "trio"
|
||||
|
||||
T_Retval = TypeVar("T_Retval")
|
||||
PosArgsT = TypeVarTuple("PosArgsT")
|
||||
|
||||
threadlocals = threading.local()
|
||||
loaded_backends: dict[str, type[AsyncBackend]] = {}
|
||||
|
||||
|
||||
def run(
|
||||
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
|
||||
*args: Unpack[PosArgsT],
|
||||
backend: str = "asyncio",
|
||||
backend_options: dict[str, Any] | None = None,
|
||||
) -> T_Retval:
|
||||
"""
|
||||
Run the given coroutine function in an asynchronous event loop.
|
||||
|
||||
The current thread must not be already running an event loop.
|
||||
|
||||
:param func: a coroutine function
|
||||
:param args: positional arguments to ``func``
|
||||
:param backend: name of the asynchronous event loop implementation – currently
|
||||
either ``asyncio`` or ``trio``
|
||||
:param backend_options: keyword arguments to call the backend ``run()``
|
||||
implementation with (documented :ref:`here <backend options>`)
|
||||
:return: the return value of the coroutine function
|
||||
:raises RuntimeError: if an asynchronous event loop is already running in this
|
||||
thread
|
||||
:raises LookupError: if the named backend is not found
|
||||
|
||||
"""
|
||||
if asynclib_name := current_async_library():
|
||||
raise RuntimeError(f"Already running {asynclib_name} in this thread")
|
||||
|
||||
try:
|
||||
async_backend = get_async_backend(backend)
|
||||
except ImportError as exc:
|
||||
if backend in BACKENDS:
|
||||
raise LookupError(
|
||||
f"Backend {backend!r} is not available. "
|
||||
f"Install it with: pip install anyio[{backend}]"
|
||||
) from exc
|
||||
|
||||
raise LookupError(f"No such backend: {backend}") from exc
|
||||
|
||||
token = None
|
||||
if asynclib_name is None:
|
||||
# Since we're in control of the event loop, we can cache the name of the async
|
||||
# library
|
||||
token = set_current_async_library(backend)
|
||||
|
||||
try:
|
||||
backend_options = backend_options or {}
|
||||
return async_backend.run(func, args, {}, backend_options)
|
||||
finally:
|
||||
reset_current_async_library(token)
|
||||
|
||||
|
||||
async def sleep(delay: float) -> None:
|
||||
"""
|
||||
Pause the current task for the specified duration.
|
||||
|
||||
:param delay: the duration, in seconds
|
||||
|
||||
"""
|
||||
return await get_async_backend().sleep(delay)
|
||||
|
||||
|
||||
async def sleep_forever() -> None:
|
||||
"""
|
||||
Pause the current task until it's cancelled.
|
||||
|
||||
This is a shortcut for ``sleep(math.inf)``.
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
"""
|
||||
await sleep(math.inf)
|
||||
|
||||
|
||||
async def sleep_until(deadline: float) -> None:
|
||||
"""
|
||||
Pause the current task until the given time.
|
||||
|
||||
:param deadline: the absolute time to wake up at (according to the internal
|
||||
monotonic clock of the event loop)
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
"""
|
||||
now = current_time()
|
||||
await sleep(max(deadline - now, 0))
|
||||
|
||||
|
||||
def current_time() -> float:
|
||||
"""
|
||||
Return the current value of the event loop's internal clock.
|
||||
|
||||
:return: the clock value (seconds)
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
return get_async_backend().current_time()
|
||||
|
||||
|
||||
def get_all_backends() -> tuple[str, ...]:
|
||||
"""Return a tuple of the names of all built-in backends."""
|
||||
return BACKENDS
|
||||
|
||||
|
||||
def get_available_backends() -> tuple[str, ...]:
|
||||
"""
|
||||
Test for the availability of built-in backends.
|
||||
|
||||
:return a tuple of the built-in backend names that were successfully imported
|
||||
|
||||
.. versionadded:: 4.12
|
||||
|
||||
"""
|
||||
available_backends: list[str] = []
|
||||
for backend_name in get_all_backends():
|
||||
try:
|
||||
get_async_backend(backend_name)
|
||||
except ImportError:
|
||||
continue
|
||||
|
||||
available_backends.append(backend_name)
|
||||
|
||||
return tuple(available_backends)
|
||||
|
||||
|
||||
def get_cancelled_exc_class() -> type[BaseException]:
|
||||
"""
|
||||
Return the current async library's cancellation exception class.
|
||||
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
return get_async_backend().cancelled_exception_class()
|
||||
|
||||
|
||||
#
|
||||
# Private API
|
||||
#
|
||||
|
||||
|
||||
@contextmanager
|
||||
def claim_worker_thread(
|
||||
backend_class: type[AsyncBackend], token: object
|
||||
) -> Generator[Any, None, None]:
|
||||
from ..lowlevel import EventLoopToken
|
||||
|
||||
threadlocals.current_token = EventLoopToken(backend_class, token)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
del threadlocals.current_token
|
||||
|
||||
|
||||
def get_async_backend(asynclib_name: str | None = None) -> type[AsyncBackend]:
|
||||
if asynclib_name is None:
|
||||
asynclib_name = current_async_library()
|
||||
if not asynclib_name:
|
||||
raise NoEventLoopError(
|
||||
f"Not currently running on any asynchronous event loop. "
|
||||
f"Available async backends: {', '.join(get_all_backends())}"
|
||||
)
|
||||
|
||||
# We use our own dict instead of sys.modules to get the already imported back-end
|
||||
# class because the appropriate modules in sys.modules could potentially be only
|
||||
# partially initialized
|
||||
try:
|
||||
return loaded_backends[asynclib_name]
|
||||
except KeyError:
|
||||
module = import_module(f"anyio._backends._{asynclib_name}")
|
||||
loaded_backends[asynclib_name] = module.backend_class
|
||||
return module.backend_class
|
||||
|
||||
|
||||
def current_async_library() -> str | None:
|
||||
if sniffio is None:
|
||||
# If sniffio is not installed, we assume we're either running asyncio or nothing
|
||||
import asyncio
|
||||
|
||||
try:
|
||||
asyncio.get_running_loop()
|
||||
return "asyncio"
|
||||
except RuntimeError:
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
return sniffio.current_async_library()
|
||||
except sniffio.AsyncLibraryNotFoundError:
|
||||
pass
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def set_current_async_library(asynclib_name: str | None) -> Token | None:
|
||||
# no-op if sniffio is not installed
|
||||
if sniffio is None:
|
||||
return None
|
||||
|
||||
return sniffio.current_async_library_cvar.set(asynclib_name)
|
||||
|
||||
|
||||
def reset_current_async_library(token: Token | None) -> None:
|
||||
if token is not None:
|
||||
sniffio.current_async_library_cvar.reset(token)
|
||||
@@ -1,205 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from collections.abc import Generator
|
||||
from textwrap import dedent
|
||||
from typing import Any
|
||||
|
||||
if sys.version_info < (3, 11):
|
||||
from exceptiongroup import BaseExceptionGroup
|
||||
|
||||
|
||||
class BrokenResourceError(Exception):
|
||||
"""
|
||||
Raised when trying to use a resource that has been rendered unusable due to external
|
||||
causes (e.g. a send stream whose peer has disconnected).
|
||||
"""
|
||||
|
||||
|
||||
class BrokenWorkerProcess(Exception):
|
||||
"""
|
||||
Raised by :meth:`~anyio.to_process.run_sync` if the worker process terminates abruptly or
|
||||
otherwise misbehaves.
|
||||
"""
|
||||
|
||||
|
||||
class BrokenWorkerInterpreter(Exception):
|
||||
"""
|
||||
Raised by :meth:`~anyio.to_interpreter.run_sync` if an unexpected exception is
|
||||
raised in the subinterpreter.
|
||||
"""
|
||||
|
||||
def __init__(self, excinfo: Any):
|
||||
# This was adapted from concurrent.futures.interpreter.ExecutionFailed
|
||||
msg = excinfo.formatted
|
||||
if not msg:
|
||||
if excinfo.type and excinfo.msg:
|
||||
msg = f"{excinfo.type.__name__}: {excinfo.msg}"
|
||||
else:
|
||||
msg = excinfo.type.__name__ or excinfo.msg
|
||||
|
||||
super().__init__(msg)
|
||||
self.excinfo = excinfo
|
||||
|
||||
def __str__(self) -> str:
|
||||
try:
|
||||
formatted = self.excinfo.errdisplay
|
||||
except Exception:
|
||||
return super().__str__()
|
||||
else:
|
||||
return dedent(
|
||||
f"""
|
||||
{super().__str__()}
|
||||
|
||||
Uncaught in the interpreter:
|
||||
|
||||
{formatted}
|
||||
""".strip()
|
||||
)
|
||||
|
||||
|
||||
class BusyResourceError(Exception):
|
||||
"""
|
||||
Raised when two tasks are trying to read from or write to the same resource
|
||||
concurrently.
|
||||
"""
|
||||
|
||||
def __init__(self, action: str):
|
||||
super().__init__(f"Another task is already {action} this resource")
|
||||
|
||||
|
||||
class ClosedResourceError(Exception):
|
||||
"""Raised when trying to use a resource that has been closed."""
|
||||
|
||||
|
||||
class ConnectionFailed(OSError):
|
||||
"""
|
||||
Raised when a connection attempt fails.
|
||||
|
||||
.. note:: This class inherits from :exc:`OSError` for backwards compatibility.
|
||||
"""
|
||||
|
||||
|
||||
def iterate_exceptions(
|
||||
exception: BaseException,
|
||||
) -> Generator[BaseException, None, None]:
|
||||
if isinstance(exception, BaseExceptionGroup):
|
||||
for exc in exception.exceptions:
|
||||
yield from iterate_exceptions(exc)
|
||||
else:
|
||||
yield exception
|
||||
|
||||
|
||||
class DelimiterNotFound(Exception):
|
||||
"""
|
||||
Raised during
|
||||
:meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
|
||||
maximum number of bytes has been read without the delimiter being found.
|
||||
"""
|
||||
|
||||
def __init__(self, max_bytes: int) -> None:
|
||||
super().__init__(
|
||||
f"The delimiter was not found among the first {max_bytes} bytes"
|
||||
)
|
||||
|
||||
|
||||
class EndOfStream(Exception):
|
||||
"""
|
||||
Raised when trying to read from a stream that has been closed from the other end.
|
||||
"""
|
||||
|
||||
|
||||
class IncompleteRead(Exception):
|
||||
"""
|
||||
Raised during
|
||||
:meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_exactly` or
|
||||
:meth:`~anyio.streams.buffered.BufferedByteReceiveStream.receive_until` if the
|
||||
connection is closed before the requested amount of bytes has been read.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(
|
||||
"The stream was closed before the read operation could be completed"
|
||||
)
|
||||
|
||||
|
||||
class TypedAttributeLookupError(LookupError):
|
||||
"""
|
||||
Raised by :meth:`~anyio.TypedAttributeProvider.extra` when the given typed attribute
|
||||
is not found and no default value has been given.
|
||||
"""
|
||||
|
||||
|
||||
class WouldBlock(Exception):
|
||||
"""Raised by ``X_nowait`` functions if ``X()`` would block."""
|
||||
|
||||
|
||||
class NoEventLoopError(RuntimeError):
|
||||
"""
|
||||
Raised by several functions that require an event loop to be running in the current
|
||||
thread when there is no running event loop.
|
||||
|
||||
This is also raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync`
|
||||
if not calling from an AnyIO worker thread, and no ``token`` was passed.
|
||||
"""
|
||||
|
||||
|
||||
class RunFinishedError(RuntimeError):
|
||||
"""
|
||||
Raised by :func:`.from_thread.run` and :func:`.from_thread.run_sync` if the event
|
||||
loop associated with the explicitly passed token has already finished.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
super().__init__(
|
||||
"The event loop associated with the given token has already finished"
|
||||
)
|
||||
|
||||
|
||||
class TaskFailed(Exception):
|
||||
"""
|
||||
Raised when awaiting on, or attempting to access the return value of, a
|
||||
:class:`.TaskHandle` that raised an exception.
|
||||
"""
|
||||
|
||||
|
||||
class TaskCancelled(TaskFailed):
|
||||
"""
|
||||
Raised when awaiting on, or attempting to access the return value of, a
|
||||
:class:`.TaskHandle` that was cancelled.
|
||||
"""
|
||||
|
||||
|
||||
class TaskNotFinished(Exception):
|
||||
"""
|
||||
Raised when attempting to access the return value or exception of a
|
||||
:class:`.TaskHandle` that is still pending completion.
|
||||
"""
|
||||
|
||||
|
||||
class FutureFailed(Exception):
|
||||
"""
|
||||
Raised when awaiting on, or attempting to access the return value of, a
|
||||
:class:`.Future` that raised an exception.
|
||||
"""
|
||||
|
||||
|
||||
class FutureCancelled(FutureFailed):
|
||||
"""
|
||||
Raised when attempting to access the return value or exception of a
|
||||
:class:`.Future` that was cancelled.
|
||||
"""
|
||||
|
||||
|
||||
class FutureNotFinished(Exception):
|
||||
"""
|
||||
Raised when attempting to access the return value or exception of a
|
||||
:class:`.Future` that is still pending completion.
|
||||
"""
|
||||
|
||||
|
||||
class FutureAlreadyFinished(Exception):
|
||||
"""
|
||||
Raised when attempting set a result of or await a
|
||||
:class:`.Future` that has already completed.
|
||||
"""
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,191 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Generator
|
||||
from enum import Enum, auto
|
||||
from typing import Any, Generic, TypeVar
|
||||
|
||||
from ._exceptions import (
|
||||
FutureAlreadyFinished,
|
||||
FutureCancelled,
|
||||
FutureFailed,
|
||||
FutureNotFinished,
|
||||
)
|
||||
from ._synchronization import Event
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class Future(Generic[T]):
|
||||
"""
|
||||
An awaitable object that works similar to a :class:`asyncio.Future` but
|
||||
with similar characteristics to a :class:`.TaskHandle`.
|
||||
"""
|
||||
|
||||
class Status(Enum):
|
||||
"""
|
||||
The status of a future handle.
|
||||
|
||||
.. attribute:: PENDING
|
||||
|
||||
The future has not finished yet.
|
||||
.. attribute:: FINISHED
|
||||
|
||||
The future has finished with a return value.
|
||||
.. attribute:: CANCELLED
|
||||
|
||||
The future was cancelled and has finished since.
|
||||
.. attribute:: FAILED
|
||||
|
||||
The future raised an exception.
|
||||
"""
|
||||
|
||||
PENDING = auto()
|
||||
FINISHED = auto()
|
||||
CANCELLED = auto()
|
||||
FAILED = auto()
|
||||
|
||||
__slots__ = (
|
||||
"_cancelled",
|
||||
"_exception",
|
||||
"_finished_event",
|
||||
"_name",
|
||||
"_return_value",
|
||||
)
|
||||
_return_value: T
|
||||
|
||||
def __init__(self, *, name: str | None = None) -> None:
|
||||
self._finished_event = Event()
|
||||
self._exception: BaseException | None = None
|
||||
self._cancelled: bool = False
|
||||
self._name = name
|
||||
|
||||
def _check_pending(self) -> None:
|
||||
"""Shortcut for checking if a Future is pending
|
||||
|
||||
:raises FutureAlreadyFinished: if future was already given a result or exception.
|
||||
:raises FutureCancelled: if future has been cancelled previously.
|
||||
"""
|
||||
match self.status:
|
||||
case Future.Status.PENDING:
|
||||
return
|
||||
case Future.Status.FINISHED:
|
||||
raise FutureAlreadyFinished("future has already finished")
|
||||
case Future.Status.FAILED:
|
||||
raise FutureAlreadyFinished("future already failed")
|
||||
case Future.Status.CANCELLED:
|
||||
raise FutureCancelled("future was cancelled")
|
||||
|
||||
async def wait(self) -> None:
|
||||
"""
|
||||
Waits for the future to finish.
|
||||
|
||||
This method will attempt to wait for a result or exception
|
||||
"""
|
||||
await self._finished_event.wait()
|
||||
|
||||
def cancel(self) -> None:
|
||||
"""Cancels a pending `.Future` object
|
||||
|
||||
Does nothing if the Future was already finished.
|
||||
"""
|
||||
if self.status is Future.Status.PENDING:
|
||||
self._cancelled = True
|
||||
self._finished_event.set()
|
||||
|
||||
@property
|
||||
def exception(self) -> BaseException | None:
|
||||
"""
|
||||
The exception value of a `.Future`
|
||||
|
||||
:raises FutureNotFinished: if future is still pending
|
||||
:raises FutureCancelled: if future was cancelled
|
||||
:returns: None if future succeeds with a result sent instead
|
||||
otherwise this will be an exception
|
||||
"""
|
||||
|
||||
match self.status:
|
||||
case Future.Status.PENDING:
|
||||
raise FutureNotFinished("the future has not finished yet")
|
||||
case Future.Status.FINISHED:
|
||||
return None
|
||||
case Future.Status.CANCELLED:
|
||||
raise FutureCancelled("the future was cancelled")
|
||||
case Future.Status.FAILED:
|
||||
return self._exception
|
||||
|
||||
@exception.setter
|
||||
def exception(self, exception: BaseException) -> None:
|
||||
"""Send exception for a `.Future` object
|
||||
|
||||
:raises FutureAlreadyFinished: if future was already given a result or exception.
|
||||
:raises FutureCancelled: if future has been cancelled previously.
|
||||
"""
|
||||
self._check_pending()
|
||||
self._exception = exception
|
||||
self._finished_event.set()
|
||||
|
||||
@property
|
||||
def return_value(self) -> T:
|
||||
"""
|
||||
The return value of the future.
|
||||
|
||||
:raises FutureNotFinished: if the future has not finished yet
|
||||
:raises FutureCancelled: if the future was cancelled
|
||||
:raises FutureFailed: if the future raised an exception
|
||||
|
||||
"""
|
||||
match self.status:
|
||||
case Future.Status.PENDING:
|
||||
raise FutureNotFinished("the future has not finished yet")
|
||||
case Future.Status.FINISHED:
|
||||
return self._return_value
|
||||
case Future.Status.CANCELLED:
|
||||
raise FutureCancelled("the future was cancelled")
|
||||
case Future.Status.FAILED:
|
||||
raise FutureFailed(
|
||||
"the future raised an exception"
|
||||
) from self._exception
|
||||
|
||||
@return_value.setter
|
||||
def return_value(self, value: T) -> None:
|
||||
"""
|
||||
Send pending result for a `.Future` object
|
||||
|
||||
:raises FutureAlreadyFinished: if future was already given a result or exception.
|
||||
:raises FutureCancelled: if future has been cancelled previously.
|
||||
"""
|
||||
self._check_pending()
|
||||
self._return_value = value
|
||||
self._finished_event.set()
|
||||
|
||||
@property
|
||||
def status(self) -> Future.Status:
|
||||
"""
|
||||
The current status of a future.
|
||||
|
||||
Every future starts in the :attr:`~Future.Status.PENDING` state.
|
||||
If a future is cancelled while in this state, it will transition to the
|
||||
:attr:`Future.Status.CANCELLED` state. When the task finishes, it will
|
||||
transition to one of the three final states (
|
||||
:attr:`Future.Status.FINISHED`, :attr:`Future.Status.FAILED`, or
|
||||
:attr:`Future.Status.CANCELLED`) depending on the exception the task
|
||||
raised, if any. No other status transitions will happen.
|
||||
"""
|
||||
if not self._finished_event.is_set():
|
||||
return Future.Status.PENDING
|
||||
elif self._cancelled:
|
||||
return Future.Status.CANCELLED
|
||||
elif self._exception is not None:
|
||||
return Future.Status.FAILED
|
||||
else:
|
||||
return Future.Status.FINISHED
|
||||
|
||||
def __await__(self) -> Generator[Any, Any, T]:
|
||||
yield from self.wait().__await__()
|
||||
return self.return_value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<{self.__class__.__name__} {self.status.name.lower()} "
|
||||
f"name={self._name!r}>"
|
||||
)
|
||||
@@ -1,18 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from ..abc import AsyncResource # noqa: TC001
|
||||
from ._tasks import CancelScope
|
||||
|
||||
|
||||
async def aclose_forcefully(resource: AsyncResource) -> None:
|
||||
"""
|
||||
Close an asynchronous resource in a cancelled scope.
|
||||
|
||||
Doing this closes the resource without waiting on anything.
|
||||
|
||||
:param resource: the resource to close
|
||||
|
||||
"""
|
||||
with CancelScope() as scope:
|
||||
scope.cancel()
|
||||
await resource.aclose()
|
||||
@@ -1,29 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterator
|
||||
from contextlib import AbstractContextManager
|
||||
from signal import Signals
|
||||
|
||||
from ._eventloop import get_async_backend
|
||||
|
||||
|
||||
def open_signal_receiver(
|
||||
*signals: Signals,
|
||||
) -> AbstractContextManager[AsyncIterator[Signals]]:
|
||||
"""
|
||||
Start receiving operating system signals.
|
||||
|
||||
:param signals: signals to receive (e.g. ``signal.SIGINT``)
|
||||
:return: an asynchronous context manager for an asynchronous iterator which yields
|
||||
signal numbers
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
.. warning:: Windows does not support signals natively so it is best to avoid
|
||||
relying on this in cross-platform applications.
|
||||
|
||||
.. warning:: On asyncio, this permanently replaces any previous signal handler for
|
||||
the given signals, as set via :meth:`~asyncio.loop.add_signal_handler`.
|
||||
|
||||
"""
|
||||
return get_async_backend().open_signal_receiver(*signals)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,52 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
from typing import TypeVar
|
||||
from warnings import warn
|
||||
|
||||
from ..streams.memory import (
|
||||
MemoryObjectReceiveStream,
|
||||
MemoryObjectSendStream,
|
||||
_MemoryObjectStreamState,
|
||||
)
|
||||
|
||||
T_Item = TypeVar("T_Item")
|
||||
|
||||
|
||||
class create_memory_object_stream(
|
||||
tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]],
|
||||
):
|
||||
"""
|
||||
Create a memory object stream.
|
||||
|
||||
The stream's item type can be annotated like
|
||||
:func:`create_memory_object_stream[T_Item]`.
|
||||
|
||||
:param max_buffer_size: number of items held in the buffer until ``send()`` starts
|
||||
blocking
|
||||
:param item_type: old way of marking the streams with the right generic type for
|
||||
static typing (does nothing on AnyIO 4)
|
||||
|
||||
.. deprecated:: 4.0
|
||||
Use ``create_memory_object_stream[YourItemType](...)`` instead.
|
||||
:return: a tuple of (send stream, receive stream)
|
||||
|
||||
"""
|
||||
|
||||
def __new__( # type: ignore[misc]
|
||||
cls, max_buffer_size: float = 0, item_type: object = None
|
||||
) -> tuple[MemoryObjectSendStream[T_Item], MemoryObjectReceiveStream[T_Item]]:
|
||||
if max_buffer_size != math.inf and not isinstance(max_buffer_size, int):
|
||||
raise ValueError("max_buffer_size must be either an integer or math.inf")
|
||||
if max_buffer_size < 0:
|
||||
raise ValueError("max_buffer_size cannot be negative")
|
||||
if item_type is not None:
|
||||
warn(
|
||||
"The item_type argument has been deprecated in AnyIO 4.0. "
|
||||
"Use create_memory_object_stream[YourItemType](...) instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
state = _MemoryObjectStreamState[T_Item](max_buffer_size)
|
||||
return (MemoryObjectSendStream(state), MemoryObjectReceiveStream(state))
|
||||
@@ -1,196 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import AsyncIterable, Iterable, Mapping, Sequence
|
||||
from io import BytesIO
|
||||
from os import PathLike
|
||||
from subprocess import PIPE, CalledProcessError, CompletedProcess
|
||||
from typing import IO, Any, TypeAlias, cast
|
||||
|
||||
from ..abc import Process # noqa: TC001
|
||||
from ._eventloop import get_async_backend
|
||||
from ._tasks import create_task_group
|
||||
|
||||
StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes]
|
||||
|
||||
|
||||
async def run_process(
|
||||
command: StrOrBytesPath | Sequence[StrOrBytesPath],
|
||||
*,
|
||||
input: bytes | None = None,
|
||||
stdin: int | IO[Any] | None = None,
|
||||
stdout: int | IO[Any] | None = PIPE,
|
||||
stderr: int | IO[Any] | None = PIPE,
|
||||
check: bool = True,
|
||||
cwd: StrOrBytesPath | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
startupinfo: Any = None,
|
||||
creationflags: int = 0,
|
||||
start_new_session: bool = False,
|
||||
pass_fds: Sequence[int] = (),
|
||||
user: str | int | None = None,
|
||||
group: str | int | None = None,
|
||||
extra_groups: Iterable[str | int] | None = None,
|
||||
umask: int = -1,
|
||||
) -> CompletedProcess[bytes]:
|
||||
"""
|
||||
Run an external command in a subprocess and wait until it completes.
|
||||
|
||||
.. seealso:: :func:`subprocess.run`
|
||||
|
||||
:param command: either a string to pass to the shell, or an iterable of strings
|
||||
containing the executable name or path and its arguments
|
||||
:param input: bytes passed to the standard input of the subprocess
|
||||
:param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
||||
a file-like object, or `None`; ``input`` overrides this
|
||||
:param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
||||
a file-like object, or `None`
|
||||
:param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
||||
:data:`subprocess.STDOUT`, a file-like object, or `None`
|
||||
:param check: if ``True``, raise :exc:`~subprocess.CalledProcessError` if the
|
||||
process terminates with a return code other than 0
|
||||
:param cwd: If not ``None``, change the working directory to this before running the
|
||||
command
|
||||
:param env: if not ``None``, this mapping replaces the inherited environment
|
||||
variables from the parent process
|
||||
:param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used
|
||||
to specify process startup parameters (Windows only)
|
||||
:param creationflags: flags that can be used to control the creation of the
|
||||
subprocess (see :class:`subprocess.Popen` for the specifics)
|
||||
:param start_new_session: if ``true`` the setsid() system call will be made in the
|
||||
child process prior to the execution of the subprocess. (POSIX only)
|
||||
:param pass_fds: sequence of file descriptors to keep open between the parent and
|
||||
child processes. (POSIX only)
|
||||
:param user: effective user to run the process as (Python >= 3.9, POSIX only)
|
||||
:param group: effective group to run the process as (Python >= 3.9, POSIX only)
|
||||
:param extra_groups: supplementary groups to set in the subprocess (Python >= 3.9,
|
||||
POSIX only)
|
||||
:param umask: if not negative, this umask is applied in the child process before
|
||||
running the given command (Python >= 3.9, POSIX only)
|
||||
:return: an object representing the completed process
|
||||
:raises ~subprocess.CalledProcessError: if ``check`` is ``True`` and the process
|
||||
exits with a nonzero return code
|
||||
|
||||
"""
|
||||
|
||||
async def drain_stream(stream: AsyncIterable[bytes], index: int) -> None:
|
||||
buffer = BytesIO()
|
||||
async for chunk in stream:
|
||||
buffer.write(chunk)
|
||||
|
||||
stream_contents[index] = buffer.getvalue()
|
||||
|
||||
if stdin is not None and input is not None:
|
||||
raise ValueError("only one of stdin and input is allowed")
|
||||
|
||||
async with await open_process(
|
||||
command,
|
||||
stdin=PIPE if input else stdin,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
startupinfo=startupinfo,
|
||||
creationflags=creationflags,
|
||||
start_new_session=start_new_session,
|
||||
pass_fds=pass_fds,
|
||||
user=user,
|
||||
group=group,
|
||||
extra_groups=extra_groups,
|
||||
umask=umask,
|
||||
) as process:
|
||||
stream_contents: list[bytes | None] = [None, None]
|
||||
async with create_task_group() as tg:
|
||||
if process.stdout:
|
||||
tg.start_soon(drain_stream, process.stdout, 0)
|
||||
|
||||
if process.stderr:
|
||||
tg.start_soon(drain_stream, process.stderr, 1)
|
||||
|
||||
if process.stdin and input:
|
||||
await process.stdin.send(input)
|
||||
await process.stdin.aclose()
|
||||
|
||||
await process.wait()
|
||||
|
||||
output, errors = stream_contents
|
||||
if check and process.returncode != 0:
|
||||
raise CalledProcessError(cast(int, process.returncode), command, output, errors)
|
||||
|
||||
return CompletedProcess(command, cast(int, process.returncode), output, errors)
|
||||
|
||||
|
||||
async def open_process(
|
||||
command: StrOrBytesPath | Sequence[StrOrBytesPath],
|
||||
*,
|
||||
stdin: int | IO[Any] | None = PIPE,
|
||||
stdout: int | IO[Any] | None = PIPE,
|
||||
stderr: int | IO[Any] | None = PIPE,
|
||||
cwd: StrOrBytesPath | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
startupinfo: Any = None,
|
||||
creationflags: int = 0,
|
||||
start_new_session: bool = False,
|
||||
pass_fds: Sequence[int] = (),
|
||||
user: str | int | None = None,
|
||||
group: str | int | None = None,
|
||||
extra_groups: Iterable[str | int] | None = None,
|
||||
umask: int = -1,
|
||||
) -> Process:
|
||||
"""
|
||||
Start an external command in a subprocess.
|
||||
|
||||
.. seealso:: :class:`subprocess.Popen`
|
||||
|
||||
:param command: either a string to pass to the shell, or an iterable of strings
|
||||
containing the executable name or path and its arguments
|
||||
:param stdin: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`, a
|
||||
file-like object, or ``None``
|
||||
:param stdout: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
||||
a file-like object, or ``None``
|
||||
:param stderr: one of :data:`subprocess.PIPE`, :data:`subprocess.DEVNULL`,
|
||||
:data:`subprocess.STDOUT`, a file-like object, or ``None``
|
||||
:param cwd: If not ``None``, the working directory is changed before executing
|
||||
:param env: If env is not ``None``, it must be a mapping that defines the
|
||||
environment variables for the new process
|
||||
:param creationflags: flags that can be used to control the creation of the
|
||||
subprocess (see :class:`subprocess.Popen` for the specifics)
|
||||
:param startupinfo: an instance of :class:`subprocess.STARTUPINFO` that can be used
|
||||
to specify process startup parameters (Windows only)
|
||||
:param start_new_session: if ``true`` the setsid() system call will be made in the
|
||||
child process prior to the execution of the subprocess. (POSIX only)
|
||||
:param pass_fds: sequence of file descriptors to keep open between the parent and
|
||||
child processes. (POSIX only)
|
||||
:param user: effective user to run the process as (POSIX only)
|
||||
:param group: effective group to run the process as (POSIX only)
|
||||
:param extra_groups: supplementary groups to set in the subprocess (POSIX only)
|
||||
:param umask: if not negative, this umask is applied in the child process before
|
||||
running the given command (POSIX only)
|
||||
:return: an asynchronous process object
|
||||
|
||||
"""
|
||||
kwargs: dict[str, Any] = {}
|
||||
if user is not None:
|
||||
kwargs["user"] = user
|
||||
|
||||
if group is not None:
|
||||
kwargs["group"] = group
|
||||
|
||||
if extra_groups is not None:
|
||||
kwargs["extra_groups"] = extra_groups
|
||||
|
||||
if umask >= 0:
|
||||
kwargs["umask"] = umask
|
||||
|
||||
return await get_async_backend().open_process(
|
||||
command,
|
||||
stdin=stdin,
|
||||
stdout=stdout,
|
||||
stderr=stderr,
|
||||
cwd=cwd,
|
||||
env=env,
|
||||
startupinfo=startupinfo,
|
||||
creationflags=creationflags,
|
||||
start_new_session=start_new_session,
|
||||
pass_fds=pass_fds,
|
||||
**kwargs,
|
||||
)
|
||||
@@ -1,778 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import sys
|
||||
from collections import deque
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass
|
||||
from types import TracebackType
|
||||
from typing import TypeVar
|
||||
|
||||
from ..lowlevel import checkpoint_if_cancelled
|
||||
from ._eventloop import get_async_backend
|
||||
from ._exceptions import BusyResourceError, NoEventLoopError
|
||||
from ._tasks import CancelScope
|
||||
from ._testing import TaskInfo, get_current_task
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import Self
|
||||
else:
|
||||
from typing_extensions import Self
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class EventStatistics:
|
||||
"""
|
||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.Event.wait`
|
||||
"""
|
||||
|
||||
tasks_waiting: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class CapacityLimiterStatistics:
|
||||
"""
|
||||
:ivar int borrowed_tokens: number of tokens currently borrowed by tasks
|
||||
:ivar float total_tokens: total number of available tokens
|
||||
:ivar tuple borrowers: tasks or other objects currently holding tokens borrowed from
|
||||
this limiter
|
||||
:ivar int tasks_waiting: number of tasks waiting on
|
||||
:meth:`~.CapacityLimiter.acquire` or
|
||||
:meth:`~.CapacityLimiter.acquire_on_behalf_of`
|
||||
"""
|
||||
|
||||
borrowed_tokens: int
|
||||
total_tokens: float
|
||||
borrowers: tuple[object, ...]
|
||||
tasks_waiting: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class LockStatistics:
|
||||
"""
|
||||
:ivar bool locked: flag indicating if this lock is locked or not
|
||||
:ivar ~anyio.TaskInfo owner: task currently holding the lock (or ``None`` if the
|
||||
lock is not held by any task)
|
||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.Lock.acquire`
|
||||
"""
|
||||
|
||||
locked: bool
|
||||
owner: TaskInfo | None
|
||||
tasks_waiting: int
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ConditionStatistics:
|
||||
"""
|
||||
:ivar int tasks_waiting: number of tasks blocked on :meth:`~.Condition.wait`
|
||||
:ivar ~anyio.LockStatistics lock_statistics: statistics of the underlying
|
||||
:class:`~.Lock`
|
||||
"""
|
||||
|
||||
tasks_waiting: int
|
||||
lock_statistics: LockStatistics
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class SemaphoreStatistics:
|
||||
"""
|
||||
:ivar int tasks_waiting: number of tasks waiting on :meth:`~.Semaphore.acquire`
|
||||
|
||||
"""
|
||||
|
||||
tasks_waiting: int
|
||||
|
||||
|
||||
class Event:
|
||||
__slots__ = ("__weakref__",)
|
||||
|
||||
def __new__(cls) -> Event:
|
||||
try:
|
||||
return get_async_backend().create_event()
|
||||
except NoEventLoopError:
|
||||
return EventAdapter()
|
||||
|
||||
def set(self) -> None:
|
||||
"""Set the flag, notifying all listeners."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_set(self) -> bool:
|
||||
"""Return ``True`` if the flag is set, ``False`` if not."""
|
||||
raise NotImplementedError
|
||||
|
||||
async def wait(self) -> None:
|
||||
"""
|
||||
Wait until the flag has been set.
|
||||
|
||||
If the flag has already been set when this method is called, it returns
|
||||
immediately.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def statistics(self) -> EventStatistics:
|
||||
"""Return statistics about the current state of this event."""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class EventAdapter(Event):
|
||||
__slots__ = "_internal_event", "_is_set"
|
||||
|
||||
def __new__(cls) -> Self:
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._internal_event: Event | None = None
|
||||
self._is_set = False
|
||||
|
||||
@property
|
||||
def _event(self) -> Event:
|
||||
if self._internal_event is None:
|
||||
self._internal_event = get_async_backend().create_event()
|
||||
if self._is_set:
|
||||
self._internal_event.set()
|
||||
|
||||
return self._internal_event
|
||||
|
||||
def set(self) -> None:
|
||||
if self._internal_event is None:
|
||||
self._is_set = True
|
||||
else:
|
||||
self._event.set()
|
||||
|
||||
def is_set(self) -> bool:
|
||||
if self._internal_event is None:
|
||||
return self._is_set
|
||||
|
||||
return self._internal_event.is_set()
|
||||
|
||||
async def wait(self) -> None:
|
||||
await self._event.wait()
|
||||
|
||||
def statistics(self) -> EventStatistics:
|
||||
if self._internal_event is None:
|
||||
return EventStatistics(tasks_waiting=0)
|
||||
|
||||
return self._internal_event.statistics()
|
||||
|
||||
|
||||
class Lock:
|
||||
__slots__ = ("__weakref__",)
|
||||
|
||||
def __new__(cls, *, fast_acquire: bool = False) -> Lock:
|
||||
try:
|
||||
return get_async_backend().create_lock(fast_acquire=fast_acquire)
|
||||
except NoEventLoopError:
|
||||
return LockAdapter(fast_acquire=fast_acquire)
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
await self.acquire()
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
self.release()
|
||||
|
||||
async def acquire(self) -> None:
|
||||
"""Acquire the lock."""
|
||||
raise NotImplementedError
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
"""
|
||||
Acquire the lock, without blocking.
|
||||
|
||||
:raises ~anyio.WouldBlock: if the operation would block
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def release(self) -> None:
|
||||
"""Release the lock."""
|
||||
raise NotImplementedError
|
||||
|
||||
def locked(self) -> bool:
|
||||
"""Return True if the lock is currently held."""
|
||||
raise NotImplementedError
|
||||
|
||||
def statistics(self) -> LockStatistics:
|
||||
"""
|
||||
Return statistics about the current state of this lock.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class LockAdapter(Lock):
|
||||
__slots__ = "_fast_acquire", "_internal_lock"
|
||||
|
||||
def __new__(cls, *, fast_acquire: bool = False) -> Self:
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, *, fast_acquire: bool = False):
|
||||
self._internal_lock: Lock | None = None
|
||||
self._fast_acquire = fast_acquire
|
||||
|
||||
@property
|
||||
def _lock(self) -> Lock:
|
||||
if self._internal_lock is None:
|
||||
self._internal_lock = get_async_backend().create_lock(
|
||||
fast_acquire=self._fast_acquire
|
||||
)
|
||||
|
||||
return self._internal_lock
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
await self._lock.acquire()
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
if self._internal_lock is not None:
|
||||
self._internal_lock.release()
|
||||
|
||||
async def acquire(self) -> None:
|
||||
"""Acquire the lock."""
|
||||
await self._lock.acquire()
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
"""
|
||||
Acquire the lock, without blocking.
|
||||
|
||||
:raises ~anyio.WouldBlock: if the operation would block
|
||||
|
||||
"""
|
||||
self._lock.acquire_nowait()
|
||||
|
||||
def release(self) -> None:
|
||||
"""Release the lock."""
|
||||
self._lock.release()
|
||||
|
||||
def locked(self) -> bool:
|
||||
"""Return True if the lock is currently held."""
|
||||
return self._lock.locked()
|
||||
|
||||
def statistics(self) -> LockStatistics:
|
||||
"""
|
||||
Return statistics about the current state of this lock.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
|
||||
"""
|
||||
if self._internal_lock is None:
|
||||
return LockStatistics(False, None, 0)
|
||||
|
||||
return self._internal_lock.statistics()
|
||||
|
||||
|
||||
class Condition:
|
||||
__slots__ = "__weakref__", "_lock", "_owner_task", "_waiters"
|
||||
|
||||
def __init__(self, lock: Lock | None = None):
|
||||
self._owner_task: TaskInfo | None = None
|
||||
self._lock = lock or Lock()
|
||||
self._waiters: deque[Event] = deque()
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
await self.acquire()
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
self.release()
|
||||
|
||||
def _check_acquired(self) -> None:
|
||||
if self._owner_task != get_current_task():
|
||||
raise RuntimeError("The current task is not holding the underlying lock")
|
||||
|
||||
async def acquire(self) -> None:
|
||||
"""Acquire the underlying lock."""
|
||||
await self._lock.acquire()
|
||||
self._owner_task = get_current_task()
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
"""
|
||||
Acquire the underlying lock, without blocking.
|
||||
|
||||
:raises ~anyio.WouldBlock: if the operation would block
|
||||
|
||||
"""
|
||||
self._lock.acquire_nowait()
|
||||
self._owner_task = get_current_task()
|
||||
|
||||
def release(self) -> None:
|
||||
"""Release the underlying lock."""
|
||||
self._lock.release()
|
||||
|
||||
def locked(self) -> bool:
|
||||
"""Return True if the lock is set."""
|
||||
return self._lock.locked()
|
||||
|
||||
def notify(self, n: int = 1) -> None:
|
||||
"""Notify exactly n listeners."""
|
||||
self._check_acquired()
|
||||
for _ in range(n):
|
||||
try:
|
||||
event = self._waiters.popleft()
|
||||
except IndexError:
|
||||
break
|
||||
|
||||
event.set()
|
||||
|
||||
def notify_all(self) -> None:
|
||||
"""Notify all the listeners."""
|
||||
self._check_acquired()
|
||||
for event in self._waiters:
|
||||
event.set()
|
||||
|
||||
self._waiters.clear()
|
||||
|
||||
async def wait(self) -> None:
|
||||
"""Wait for a notification."""
|
||||
await checkpoint_if_cancelled()
|
||||
self._check_acquired()
|
||||
event = Event()
|
||||
self._waiters.append(event)
|
||||
self.release()
|
||||
try:
|
||||
await event.wait()
|
||||
except BaseException:
|
||||
if not event.is_set():
|
||||
self._waiters.remove(event)
|
||||
elif self._waiters:
|
||||
# This task was notified by could not act on it, so pass
|
||||
# it on to the next task
|
||||
self._waiters.popleft().set()
|
||||
|
||||
raise
|
||||
finally:
|
||||
with CancelScope(shield=True):
|
||||
await self.acquire()
|
||||
|
||||
async def wait_for(self, predicate: Callable[[], T]) -> T:
|
||||
"""
|
||||
Wait until a predicate becomes true.
|
||||
|
||||
:param predicate: a callable that returns a truthy value when the condition is
|
||||
met
|
||||
:return: the result of the predicate
|
||||
|
||||
.. versionadded:: 4.11.0
|
||||
|
||||
"""
|
||||
while not (result := predicate()):
|
||||
await self.wait()
|
||||
|
||||
return result
|
||||
|
||||
def statistics(self) -> ConditionStatistics:
|
||||
"""
|
||||
Return statistics about the current state of this condition.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
return ConditionStatistics(len(self._waiters), self._lock.statistics())
|
||||
|
||||
|
||||
class Semaphore:
|
||||
__slots__ = "__weakref__", "_fast_acquire"
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
initial_value: int,
|
||||
*,
|
||||
max_value: int | None = None,
|
||||
fast_acquire: bool = False,
|
||||
) -> Semaphore:
|
||||
try:
|
||||
return get_async_backend().create_semaphore(
|
||||
initial_value, max_value=max_value, fast_acquire=fast_acquire
|
||||
)
|
||||
except NoEventLoopError:
|
||||
return SemaphoreAdapter(initial_value, max_value=max_value)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initial_value: int,
|
||||
*,
|
||||
max_value: int | None = None,
|
||||
fast_acquire: bool = False,
|
||||
):
|
||||
if not isinstance(initial_value, int):
|
||||
raise TypeError("initial_value must be an integer")
|
||||
if initial_value < 0:
|
||||
raise ValueError("initial_value must be >= 0")
|
||||
if max_value is not None:
|
||||
if not isinstance(max_value, int):
|
||||
raise TypeError("max_value must be an integer or None")
|
||||
if max_value < initial_value:
|
||||
raise ValueError(
|
||||
"max_value must be equal to or higher than initial_value"
|
||||
)
|
||||
|
||||
self._fast_acquire = fast_acquire
|
||||
|
||||
async def __aenter__(self) -> Self:
|
||||
await self.acquire()
|
||||
return self
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
self.release()
|
||||
|
||||
async def acquire(self) -> None:
|
||||
"""Decrement the semaphore value, blocking if necessary."""
|
||||
raise NotImplementedError
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
"""
|
||||
Acquire the underlying lock, without blocking.
|
||||
|
||||
:raises ~anyio.WouldBlock: if the operation would block
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def release(self) -> None:
|
||||
"""Increment the semaphore value."""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
"""The current value of the semaphore."""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def max_value(self) -> int | None:
|
||||
"""The maximum value of the semaphore."""
|
||||
raise NotImplementedError
|
||||
|
||||
def statistics(self) -> SemaphoreStatistics:
|
||||
"""
|
||||
Return statistics about the current state of this semaphore.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class SemaphoreAdapter(Semaphore):
|
||||
__slots__ = "_initial_value", "_internal_semaphore", "_max_value"
|
||||
|
||||
def __new__(
|
||||
cls,
|
||||
initial_value: int,
|
||||
*,
|
||||
max_value: int | None = None,
|
||||
fast_acquire: bool = False,
|
||||
) -> Self:
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
initial_value: int,
|
||||
*,
|
||||
max_value: int | None = None,
|
||||
fast_acquire: bool = False,
|
||||
) -> None:
|
||||
super().__init__(initial_value, max_value=max_value, fast_acquire=fast_acquire)
|
||||
self._internal_semaphore: Semaphore | None = None
|
||||
self._initial_value = initial_value
|
||||
self._max_value = max_value
|
||||
|
||||
@property
|
||||
def _semaphore(self) -> Semaphore:
|
||||
if self._internal_semaphore is None:
|
||||
self._internal_semaphore = get_async_backend().create_semaphore(
|
||||
self._initial_value, max_value=self._max_value
|
||||
)
|
||||
|
||||
return self._internal_semaphore
|
||||
|
||||
async def acquire(self) -> None:
|
||||
await self._semaphore.acquire()
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
self._semaphore.acquire_nowait()
|
||||
|
||||
def release(self) -> None:
|
||||
self._semaphore.release()
|
||||
|
||||
@property
|
||||
def value(self) -> int:
|
||||
if self._internal_semaphore is None:
|
||||
return self._initial_value
|
||||
|
||||
return self._semaphore.value
|
||||
|
||||
@property
|
||||
def max_value(self) -> int | None:
|
||||
return self._max_value
|
||||
|
||||
def statistics(self) -> SemaphoreStatistics:
|
||||
if self._internal_semaphore is None:
|
||||
return SemaphoreStatistics(tasks_waiting=0)
|
||||
|
||||
return self._semaphore.statistics()
|
||||
|
||||
|
||||
class CapacityLimiter:
|
||||
__slots__ = ("__weakref__",)
|
||||
|
||||
def __new__(cls, total_tokens: float) -> CapacityLimiter:
|
||||
try:
|
||||
return get_async_backend().create_capacity_limiter(total_tokens)
|
||||
except NoEventLoopError:
|
||||
return CapacityLimiterAdapter(total_tokens)
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def total_tokens(self) -> float:
|
||||
"""
|
||||
The total number of tokens available for borrowing.
|
||||
|
||||
This is a read-write property. If the total number of tokens is increased, the
|
||||
proportionate number of tasks waiting on this limiter will be granted their
|
||||
tokens.
|
||||
|
||||
.. versionchanged:: 3.0
|
||||
The property is now writable.
|
||||
.. versionchanged:: 4.12
|
||||
The value can now be set to 0.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@total_tokens.setter
|
||||
def total_tokens(self, value: float) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def borrowed_tokens(self) -> int:
|
||||
"""The number of tokens that have currently been borrowed."""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def available_tokens(self) -> float:
|
||||
"""The number of tokens currently available to be borrowed"""
|
||||
raise NotImplementedError
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
"""
|
||||
Acquire a token for the current task without waiting for one to become
|
||||
available.
|
||||
|
||||
:raises ~anyio.WouldBlock: if there are no tokens available for borrowing
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def acquire_on_behalf_of_nowait(self, borrower: object) -> None:
|
||||
"""
|
||||
Acquire a token without waiting for one to become available.
|
||||
|
||||
:param borrower: the entity borrowing a token
|
||||
:raises ~anyio.WouldBlock: if there are no tokens available for borrowing
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
async def acquire(self) -> None:
|
||||
"""
|
||||
Acquire a token for the current task, waiting if necessary for one to become
|
||||
available.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
async def acquire_on_behalf_of(self, borrower: object) -> None:
|
||||
"""
|
||||
Acquire a token, waiting if necessary for one to become available.
|
||||
|
||||
:param borrower: the entity borrowing a token
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def release(self) -> None:
|
||||
"""
|
||||
Release the token held by the current task.
|
||||
|
||||
:raises RuntimeError: if the current task has not borrowed a token from this
|
||||
limiter.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def release_on_behalf_of(self, borrower: object) -> None:
|
||||
"""
|
||||
Release the token held by the given borrower.
|
||||
|
||||
:raises RuntimeError: if the borrower has not borrowed a token from this
|
||||
limiter.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def statistics(self) -> CapacityLimiterStatistics:
|
||||
"""
|
||||
Return statistics about the current state of this limiter.
|
||||
|
||||
.. versionadded:: 3.0
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CapacityLimiterAdapter(CapacityLimiter):
|
||||
__slots__ = "_internal_limiter", "_total_tokens"
|
||||
|
||||
def __new__(cls, total_tokens: float) -> Self:
|
||||
return object.__new__(cls)
|
||||
|
||||
def __init__(self, total_tokens: float) -> None:
|
||||
self._internal_limiter: CapacityLimiter | None = None
|
||||
self.total_tokens = total_tokens
|
||||
|
||||
@property
|
||||
def _limiter(self) -> CapacityLimiter:
|
||||
if self._internal_limiter is None:
|
||||
self._internal_limiter = get_async_backend().create_capacity_limiter(
|
||||
self._total_tokens
|
||||
)
|
||||
|
||||
return self._internal_limiter
|
||||
|
||||
async def __aenter__(self) -> None:
|
||||
await self._limiter.__aenter__()
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
return await self._limiter.__aexit__(exc_type, exc_val, exc_tb)
|
||||
|
||||
@property
|
||||
def total_tokens(self) -> float:
|
||||
if self._internal_limiter is None:
|
||||
return self._total_tokens
|
||||
|
||||
return self._internal_limiter.total_tokens
|
||||
|
||||
@total_tokens.setter
|
||||
def total_tokens(self, value: float) -> None:
|
||||
if not isinstance(value, int) and not math.isinf(value):
|
||||
raise TypeError("total_tokens must be an int or math.inf")
|
||||
elif value < 0:
|
||||
raise ValueError("total_tokens must be >= 0")
|
||||
|
||||
if self._internal_limiter is None:
|
||||
self._total_tokens = value
|
||||
return
|
||||
|
||||
self._limiter.total_tokens = value
|
||||
|
||||
@property
|
||||
def borrowed_tokens(self) -> int:
|
||||
if self._internal_limiter is None:
|
||||
return 0
|
||||
|
||||
return self._internal_limiter.borrowed_tokens
|
||||
|
||||
@property
|
||||
def available_tokens(self) -> float:
|
||||
if self._internal_limiter is None:
|
||||
return self._total_tokens
|
||||
|
||||
return self._internal_limiter.available_tokens
|
||||
|
||||
def acquire_nowait(self) -> None:
|
||||
self._limiter.acquire_nowait()
|
||||
|
||||
def acquire_on_behalf_of_nowait(self, borrower: object) -> None:
|
||||
self._limiter.acquire_on_behalf_of_nowait(borrower)
|
||||
|
||||
async def acquire(self) -> None:
|
||||
await self._limiter.acquire()
|
||||
|
||||
async def acquire_on_behalf_of(self, borrower: object) -> None:
|
||||
await self._limiter.acquire_on_behalf_of(borrower)
|
||||
|
||||
def release(self) -> None:
|
||||
self._limiter.release()
|
||||
|
||||
def release_on_behalf_of(self, borrower: object) -> None:
|
||||
self._limiter.release_on_behalf_of(borrower)
|
||||
|
||||
def statistics(self) -> CapacityLimiterStatistics:
|
||||
if self._internal_limiter is None:
|
||||
return CapacityLimiterStatistics(
|
||||
borrowed_tokens=0,
|
||||
total_tokens=self.total_tokens,
|
||||
borrowers=(),
|
||||
tasks_waiting=0,
|
||||
)
|
||||
|
||||
return self._internal_limiter.statistics()
|
||||
|
||||
|
||||
class ResourceGuard:
|
||||
"""
|
||||
A context manager for ensuring that a resource is only used by a single task at a
|
||||
time.
|
||||
|
||||
Entering this context manager while the previous has not exited it yet will trigger
|
||||
:exc:`BusyResourceError`.
|
||||
|
||||
:param action: the action to guard against (visible in the :exc:`BusyResourceError`
|
||||
when triggered, e.g. "Another task is already {action} this resource")
|
||||
|
||||
.. versionadded:: 4.1
|
||||
"""
|
||||
|
||||
__slots__ = "__weakref__", "_guarded", "action"
|
||||
|
||||
def __init__(self, action: str = "using"):
|
||||
self.action: str = action
|
||||
self._guarded = False
|
||||
|
||||
def __enter__(self) -> None:
|
||||
if self._guarded:
|
||||
raise BusyResourceError(self.action)
|
||||
|
||||
self._guarded = True
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
self._guarded = False
|
||||
@@ -1,464 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import sys
|
||||
from collections.abc import (
|
||||
Coroutine,
|
||||
Generator,
|
||||
)
|
||||
from contextlib import contextmanager
|
||||
from enum import Enum, auto
|
||||
from inspect import iscoroutine
|
||||
from types import TracebackType
|
||||
from typing import Any, Generic, final
|
||||
|
||||
from ..abc import TaskGroup, TaskStatus
|
||||
from ._eventloop import get_async_backend, get_cancelled_exc_class
|
||||
from ._exceptions import TaskCancelled, TaskFailed, TaskNotFinished
|
||||
|
||||
if sys.version_info >= (3, 13):
|
||||
from typing import TypeVar
|
||||
else:
|
||||
from typing_extensions import TypeVar
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import Never, Self, TypeVarTuple
|
||||
else:
|
||||
from typing_extensions import Never, Self, TypeVarTuple
|
||||
|
||||
T = TypeVar("T")
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
T_startval_co = TypeVar("T_startval_co", covariant=True, default=Never)
|
||||
PosArgsT = TypeVarTuple("PosArgsT")
|
||||
|
||||
|
||||
class _IgnoredTaskStatus(TaskStatus[object]):
|
||||
def started(self, value: object = None) -> None:
|
||||
pass
|
||||
|
||||
|
||||
TASK_STATUS_IGNORED = _IgnoredTaskStatus()
|
||||
|
||||
|
||||
class CancelScope:
|
||||
"""
|
||||
Wraps a unit of work that can be made separately cancellable.
|
||||
|
||||
:param deadline: The time (clock value) when this scope is cancelled automatically
|
||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
"""
|
||||
|
||||
__slots__ = ("__weakref__",)
|
||||
|
||||
def __new__(
|
||||
cls, *, deadline: float = math.inf, shield: bool = False
|
||||
) -> CancelScope:
|
||||
return get_async_backend().create_cancel_scope(shield=shield, deadline=deadline)
|
||||
|
||||
def cancel(self, reason: str | None = None) -> None:
|
||||
"""
|
||||
Cancel this scope immediately.
|
||||
|
||||
:param reason: a message describing the reason for the cancellation
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def deadline(self) -> float:
|
||||
"""
|
||||
The time (clock value) when this scope is cancelled automatically.
|
||||
|
||||
Will be ``float('inf')`` if no timeout has been set.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@deadline.setter
|
||||
def deadline(self, value: float) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def cancel_called(self) -> bool:
|
||||
"""``True`` if :meth:`cancel` has been called."""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def cancelled_caught(self) -> bool:
|
||||
"""
|
||||
``True`` if this scope suppressed a cancellation exception it itself raised.
|
||||
|
||||
This is typically used to check if any work was interrupted, or to see if the
|
||||
scope was cancelled due to its deadline being reached. The value will, however,
|
||||
only be ``True`` if the cancellation was triggered by the scope itself (and not
|
||||
an outer scope).
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def shield(self) -> bool:
|
||||
"""
|
||||
``True`` if this scope is shielded from external cancellation.
|
||||
|
||||
While a scope is shielded, it will not receive cancellations from outside.
|
||||
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
@shield.setter
|
||||
def shield(self, value: bool) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def __enter__(self) -> Self:
|
||||
raise NotImplementedError
|
||||
|
||||
def __exit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
@contextmanager
|
||||
def fail_at(
|
||||
deadline: float | None, shield: bool = False, reason: str | None = None
|
||||
) -> Generator[CancelScope, None, None]:
|
||||
"""
|
||||
Create a context manager which raises a :class:`TimeoutError` if the code in the
|
||||
enclosing context does not finish by the given deadline.
|
||||
|
||||
:param deadline: the deadline before raising the exception, or
|
||||
``None`` to disable the timeout
|
||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
||||
:param reason: explanation for timeout to add to the message of a raised `TimeoutError`
|
||||
:return: a context manager that yields a cancel scope
|
||||
:rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\]
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
.. versionadded:: 4.15.0
|
||||
|
||||
"""
|
||||
current_time = get_async_backend().current_time
|
||||
effective_deadline = math.inf if deadline is None else deadline
|
||||
with get_async_backend().create_cancel_scope(
|
||||
deadline=effective_deadline, shield=shield
|
||||
) as cancel_scope:
|
||||
yield cancel_scope
|
||||
|
||||
if cancel_scope.cancelled_caught and current_time() >= cancel_scope.deadline:
|
||||
raise TimeoutError(reason) if reason else TimeoutError
|
||||
|
||||
|
||||
@contextmanager
|
||||
def fail_after(
|
||||
delay: float | None, shield: bool = False, reason: str | None = None
|
||||
) -> Generator[CancelScope, None, None]:
|
||||
"""
|
||||
Create a context manager which raises a :class:`TimeoutError` if the code in the
|
||||
enclosing context block does not finish in time.
|
||||
|
||||
:param delay: maximum allowed time (in seconds) before raising the exception, or
|
||||
``None`` to disable the timeout
|
||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
||||
:param reason: explanation for timeout to add to the message of a raised `TimeoutError`
|
||||
:return: a context manager that yields a cancel scope
|
||||
:rtype: :class:`~typing.ContextManager`\\[:class:`~anyio.CancelScope`\\]
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
current_time = get_async_backend().current_time
|
||||
deadline = (current_time() + delay) if delay is not None else math.inf
|
||||
with fail_at(deadline, shield=shield, reason=reason) as scope:
|
||||
yield scope
|
||||
|
||||
|
||||
def move_on_at(deadline: float | None, shield: bool = False) -> CancelScope:
|
||||
"""
|
||||
Create a cancel scope with a deadline that expires after the given delay.
|
||||
|
||||
:param deadline: the deadline before exiting the context block, or
|
||||
``None`` to disable the timeout
|
||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
||||
:return: a cancel scope
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
.. versionadded:: 4.15.0
|
||||
|
||||
"""
|
||||
return get_async_backend().create_cancel_scope(
|
||||
deadline=deadline if deadline is not None else math.inf, shield=shield
|
||||
)
|
||||
|
||||
|
||||
def move_on_after(delay: float | None, shield: bool = False) -> CancelScope:
|
||||
"""
|
||||
Create a cancel scope with a deadline that expires after the given delay.
|
||||
|
||||
:param delay: maximum allowed time (in seconds) before exiting the context block, or
|
||||
``None`` to disable the timeout
|
||||
:param shield: ``True`` to shield the cancel scope from external cancellation
|
||||
:return: a cancel scope
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
.. note:: Unlike with :func:`fail_after`, the timer starts when this function is
|
||||
called, not when the context manager is entered. This will be changed in v5.0.
|
||||
|
||||
"""
|
||||
deadline = (
|
||||
(get_async_backend().current_time() + delay) if delay is not None else math.inf
|
||||
)
|
||||
return get_async_backend().create_cancel_scope(deadline=deadline, shield=shield)
|
||||
|
||||
|
||||
def current_effective_deadline() -> float:
|
||||
"""
|
||||
Return the nearest deadline among all the cancel scopes effective for the current
|
||||
task.
|
||||
|
||||
:return: a clock value from the event loop's internal clock (or ``float('inf')`` if
|
||||
there is no deadline in effect, or ``float('-inf')`` if the current scope has
|
||||
been cancelled)
|
||||
:rtype: float
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
return get_async_backend().current_effective_deadline()
|
||||
|
||||
|
||||
def create_task_group() -> TaskGroup:
|
||||
"""
|
||||
Create a task group.
|
||||
|
||||
:return: a task group
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
return get_async_backend().create_task_group()
|
||||
|
||||
|
||||
@final
|
||||
class TaskHandle(Generic[T_co, T_startval_co]):
|
||||
"""
|
||||
Returned from the task-spawning methods of :class:`TaskGroup`. Can be awaited on to
|
||||
get the return value of the task (or the raised exception). If the task was
|
||||
terminated by a :exc:`BaseException`, :exc:`TaskFailed` will be raised (or its
|
||||
subclass :exc:`TaskCancelled` if the task was cancelled).
|
||||
|
||||
.. versionadded:: 4.14.0
|
||||
"""
|
||||
|
||||
class Status(Enum):
|
||||
"""
|
||||
The status of a task handle.
|
||||
|
||||
.. attribute:: PENDING
|
||||
|
||||
The task has not finished yet.
|
||||
.. attribute:: FINISHED
|
||||
|
||||
The task has finished with a return value.
|
||||
.. attribute:: CANCELLING
|
||||
|
||||
The task has been cancelled but has not finished yet.
|
||||
.. attribute:: CANCELLED
|
||||
|
||||
The task was cancelled and has finished since.
|
||||
.. attribute:: FAILED
|
||||
|
||||
The task raised an exception.
|
||||
"""
|
||||
|
||||
PENDING = auto()
|
||||
FINISHED = auto()
|
||||
CANCELLING = auto()
|
||||
CANCELLED = auto()
|
||||
FAILED = auto()
|
||||
|
||||
__slots__ = (
|
||||
"__weakref__",
|
||||
"_cancel_scope",
|
||||
"_coro",
|
||||
"_exception",
|
||||
"_finished_event",
|
||||
"_name",
|
||||
"_return_value",
|
||||
"_start_value",
|
||||
)
|
||||
|
||||
_return_value: T_co
|
||||
_start_value: T_startval_co
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coro: Coroutine[Any, Any, T_co],
|
||||
name: object,
|
||||
cancel_scope: CancelScope | None = None,
|
||||
) -> None:
|
||||
from ._synchronization import Event
|
||||
|
||||
self._coro = coro
|
||||
self._cancel_scope = cancel_scope if cancel_scope is not None else CancelScope()
|
||||
self._finished_event = Event()
|
||||
self._exception: BaseException | None = None
|
||||
|
||||
if name is not None:
|
||||
self._name = str(name)
|
||||
elif iscoroutine(coro):
|
||||
self._name = coro.__qualname__
|
||||
else:
|
||||
self._name = str(coro) # coroutine-like object (e.g. asend() objects)
|
||||
|
||||
async def _run_coro(self) -> None:
|
||||
__tracebackhide__ = True
|
||||
|
||||
with self._cancel_scope:
|
||||
try:
|
||||
retval = await self._coro
|
||||
except BaseException as exc:
|
||||
self._exception = exc
|
||||
raise
|
||||
else:
|
||||
self._return_value = retval
|
||||
finally:
|
||||
self._finished_event.set()
|
||||
del self # Break the reference cycle
|
||||
|
||||
def cancel(self) -> None:
|
||||
"""
|
||||
Set the task to a cancelled state.
|
||||
|
||||
This will interrupt any interruptible asynchronous operation, and will cause
|
||||
any further awaits on this task to get immediately cancelled, unless done in
|
||||
a shielded cancel scope.
|
||||
|
||||
If the task has already finished, this method has no effect.
|
||||
"""
|
||||
if not self._finished_event.is_set():
|
||||
self._cancel_scope.cancel()
|
||||
|
||||
@property
|
||||
def coro(self) -> Coroutine[Any, Any, T_co]:
|
||||
"""
|
||||
The coroutine object that was passed to one of the task-spawning methods in
|
||||
:class:`TaskGroup`.
|
||||
"""
|
||||
return self._coro
|
||||
|
||||
@property
|
||||
def status(self) -> TaskHandle.Status:
|
||||
"""
|
||||
The current status of the task.
|
||||
|
||||
Every task starts in the :attr:`~TaskHandle.Status.PENDING` state.
|
||||
If a task is cancelled while in this state, it will transition to the
|
||||
:attr:`~TaskHandle.Status.CANCELLING` state. When the task finishes, it will
|
||||
transition to one of the three final states (
|
||||
:attr:`~TaskHandle.Status.FINISHED`, :attr:`~TaskHandle.Status.FAILED`, or
|
||||
:attr:`~TaskHandle.Status.CANCELLING`) depending on the exception the task
|
||||
raised, if any. No other status transitions will happen.
|
||||
"""
|
||||
if not self._finished_event.is_set():
|
||||
if self._cancel_scope.cancel_called:
|
||||
return TaskHandle.Status.CANCELLING
|
||||
else:
|
||||
return TaskHandle.Status.PENDING
|
||||
elif self._exception is not None:
|
||||
if isinstance(self._exception, get_cancelled_exc_class()):
|
||||
return TaskHandle.Status.CANCELLED
|
||||
else:
|
||||
return TaskHandle.Status.FAILED
|
||||
else:
|
||||
return TaskHandle.Status.FINISHED
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
"""The name of the task."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def exception(self) -> BaseException | None:
|
||||
"""
|
||||
The exception raised by the task, or ``None`` if it finished without raising.
|
||||
|
||||
:raises TaskNotFinished: if the task has not finished yet
|
||||
:raises TaskCancelled: if the task was cancelled
|
||||
|
||||
"""
|
||||
match self.status:
|
||||
case TaskHandle.Status.PENDING:
|
||||
raise TaskNotFinished("the task has not finished yet")
|
||||
case TaskHandle.Status.FINISHED:
|
||||
return None
|
||||
case TaskHandle.Status.CANCELLING:
|
||||
raise TaskCancelled("the task was cancelled")
|
||||
case TaskHandle.Status.CANCELLED:
|
||||
raise TaskCancelled("the task was cancelled") from self._exception
|
||||
case TaskHandle.Status.FAILED:
|
||||
return self._exception
|
||||
|
||||
@property
|
||||
def return_value(self) -> T_co:
|
||||
"""
|
||||
The return value of the task.
|
||||
|
||||
:raises TaskNotFinished: if the task has not finished yet
|
||||
:raises TaskCancelled: if the task was cancelled
|
||||
:raises TaskFailed: if the task raised an exception
|
||||
|
||||
"""
|
||||
match self.status:
|
||||
case TaskHandle.Status.PENDING:
|
||||
raise TaskNotFinished("the task has not finished yet")
|
||||
case TaskHandle.Status.FINISHED:
|
||||
return self._return_value
|
||||
case TaskHandle.Status.CANCELLING:
|
||||
raise TaskCancelled("the task was cancelled")
|
||||
case TaskHandle.Status.CANCELLED:
|
||||
raise TaskCancelled("the task was cancelled") from self._exception
|
||||
case TaskHandle.Status.FAILED:
|
||||
raise TaskFailed("the task raised an exception") from self._exception
|
||||
|
||||
@property
|
||||
def start_value(self) -> T_startval_co:
|
||||
"""
|
||||
The value passed to :meth:`task_status.started() <.abc.TaskStatus.started>`,
|
||||
|
||||
:raises RuntimeError: if the task was not started with :meth:`TaskGroup.start()
|
||||
<.abc.TaskGroup.start>`
|
||||
"""
|
||||
try:
|
||||
return self._start_value
|
||||
except AttributeError:
|
||||
raise RuntimeError(
|
||||
"the task was not started with TaskGroup.start()"
|
||||
) from None
|
||||
|
||||
async def wait(self) -> None:
|
||||
"""
|
||||
Wait for the task to finish.
|
||||
|
||||
This method will return as soon as the task has finished, no matter how it
|
||||
happened.
|
||||
"""
|
||||
await self._finished_event.wait()
|
||||
|
||||
def __await__(self) -> Generator[Any, Any, T_co]:
|
||||
yield from self._finished_event.wait().__await__()
|
||||
return self.return_value
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<{self.__class__.__name__} {self.status.name.lower()} "
|
||||
f"name={self._name!r} coro={self._coro!r}>"
|
||||
)
|
||||
@@ -1,615 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
from collections.abc import Iterable
|
||||
from io import BytesIO, TextIOWrapper
|
||||
from types import TracebackType
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
AnyStr,
|
||||
Generic,
|
||||
overload,
|
||||
)
|
||||
|
||||
from .. import to_thread
|
||||
from .._core._fileio import AsyncFile
|
||||
from ..lowlevel import checkpoint_if_cancelled
|
||||
from ._tasks import CancelScope
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import OpenBinaryMode, OpenTextMode, ReadableBuffer, WriteableBuffer
|
||||
|
||||
|
||||
class TemporaryFile(Generic[AnyStr]):
|
||||
"""
|
||||
An asynchronous temporary file that is automatically created and cleaned up.
|
||||
|
||||
This class provides an asynchronous context manager interface to a temporary file.
|
||||
The file is created using Python's standard `tempfile.TemporaryFile` function in a
|
||||
background thread, and is wrapped as an asynchronous file using `AsyncFile`.
|
||||
|
||||
:param mode: The mode in which the file is opened. Defaults to "w+b".
|
||||
:param buffering: The buffering policy (-1 means the default buffering).
|
||||
:param encoding: The encoding used to decode or encode the file. Only applicable in
|
||||
text mode.
|
||||
:param newline: Controls how universal newlines mode works (only applicable in text
|
||||
mode).
|
||||
:param suffix: The suffix for the temporary file name.
|
||||
:param prefix: The prefix for the temporary file name.
|
||||
:param dir: The directory in which the temporary file is created.
|
||||
:param errors: The error handling scheme used for encoding/decoding errors.
|
||||
"""
|
||||
|
||||
_async_file: AsyncFile[AnyStr]
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self: TemporaryFile[bytes],
|
||||
mode: OpenBinaryMode = ...,
|
||||
buffering: int = ...,
|
||||
encoding: str | None = ...,
|
||||
newline: str | None = ...,
|
||||
suffix: str | None = ...,
|
||||
prefix: str | None = ...,
|
||||
dir: str | None = ...,
|
||||
*,
|
||||
errors: str | None = ...,
|
||||
): ...
|
||||
@overload
|
||||
def __init__(
|
||||
self: TemporaryFile[str],
|
||||
mode: OpenTextMode,
|
||||
buffering: int = ...,
|
||||
encoding: str | None = ...,
|
||||
newline: str | None = ...,
|
||||
suffix: str | None = ...,
|
||||
prefix: str | None = ...,
|
||||
dir: str | None = ...,
|
||||
*,
|
||||
errors: str | None = ...,
|
||||
): ...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mode: OpenTextMode | OpenBinaryMode = "w+b",
|
||||
buffering: int = -1,
|
||||
encoding: str | None = None,
|
||||
newline: str | None = None,
|
||||
suffix: str | None = None,
|
||||
prefix: str | None = None,
|
||||
dir: str | None = None,
|
||||
*,
|
||||
errors: str | None = None,
|
||||
) -> None:
|
||||
self.mode = mode
|
||||
self.buffering = buffering
|
||||
self.encoding = encoding
|
||||
self.newline = newline
|
||||
self.suffix: str | None = suffix
|
||||
self.prefix: str | None = prefix
|
||||
self.dir: str | None = dir
|
||||
self.errors = errors
|
||||
|
||||
async def __aenter__(self) -> AsyncFile[AnyStr]:
|
||||
fp = await to_thread.run_sync(
|
||||
lambda: tempfile.TemporaryFile(
|
||||
self.mode,
|
||||
self.buffering,
|
||||
self.encoding,
|
||||
self.newline,
|
||||
self.suffix,
|
||||
self.prefix,
|
||||
self.dir,
|
||||
errors=self.errors,
|
||||
)
|
||||
)
|
||||
self._async_file = AsyncFile(fp)
|
||||
return self._async_file
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_value: BaseException | None,
|
||||
traceback: TracebackType | None,
|
||||
) -> None:
|
||||
await self._async_file.aclose()
|
||||
|
||||
|
||||
class NamedTemporaryFile(Generic[AnyStr]):
|
||||
"""
|
||||
An asynchronous named temporary file that is automatically created and cleaned up.
|
||||
|
||||
This class provides an asynchronous context manager for a temporary file with a
|
||||
visible name in the file system. It uses Python's standard
|
||||
:func:`~tempfile.NamedTemporaryFile` function and wraps the file object with
|
||||
:class:`AsyncFile` for asynchronous operations.
|
||||
|
||||
:param mode: The mode in which the file is opened. Defaults to "w+b".
|
||||
:param buffering: The buffering policy (-1 means the default buffering).
|
||||
:param encoding: The encoding used to decode or encode the file. Only applicable in
|
||||
text mode.
|
||||
:param newline: Controls how universal newlines mode works (only applicable in text
|
||||
mode).
|
||||
:param suffix: The suffix for the temporary file name.
|
||||
:param prefix: The prefix for the temporary file name.
|
||||
:param dir: The directory in which the temporary file is created.
|
||||
:param delete: Whether to delete the file when it is closed.
|
||||
:param errors: The error handling scheme used for encoding/decoding errors.
|
||||
:param delete_on_close: (Python 3.12+) Whether to delete the file on close.
|
||||
"""
|
||||
|
||||
_async_file: AsyncFile[AnyStr]
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self: NamedTemporaryFile[bytes],
|
||||
mode: OpenBinaryMode = ...,
|
||||
buffering: int = ...,
|
||||
encoding: str | None = ...,
|
||||
newline: str | None = ...,
|
||||
suffix: str | None = ...,
|
||||
prefix: str | None = ...,
|
||||
dir: str | None = ...,
|
||||
delete: bool = ...,
|
||||
*,
|
||||
errors: str | None = ...,
|
||||
delete_on_close: bool = ...,
|
||||
): ...
|
||||
@overload
|
||||
def __init__(
|
||||
self: NamedTemporaryFile[str],
|
||||
mode: OpenTextMode,
|
||||
buffering: int = ...,
|
||||
encoding: str | None = ...,
|
||||
newline: str | None = ...,
|
||||
suffix: str | None = ...,
|
||||
prefix: str | None = ...,
|
||||
dir: str | None = ...,
|
||||
delete: bool = ...,
|
||||
*,
|
||||
errors: str | None = ...,
|
||||
delete_on_close: bool = ...,
|
||||
): ...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
mode: OpenBinaryMode | OpenTextMode = "w+b",
|
||||
buffering: int = -1,
|
||||
encoding: str | None = None,
|
||||
newline: str | None = None,
|
||||
suffix: str | None = None,
|
||||
prefix: str | None = None,
|
||||
dir: str | None = None,
|
||||
delete: bool = True,
|
||||
*,
|
||||
errors: str | None = None,
|
||||
delete_on_close: bool = True,
|
||||
) -> None:
|
||||
self._params: dict[str, Any] = {
|
||||
"mode": mode,
|
||||
"buffering": buffering,
|
||||
"encoding": encoding,
|
||||
"newline": newline,
|
||||
"suffix": suffix,
|
||||
"prefix": prefix,
|
||||
"dir": dir,
|
||||
"delete": delete,
|
||||
"errors": errors,
|
||||
}
|
||||
if sys.version_info >= (3, 12):
|
||||
self._params["delete_on_close"] = delete_on_close
|
||||
|
||||
async def __aenter__(self) -> AsyncFile[AnyStr]:
|
||||
fp = await to_thread.run_sync(
|
||||
lambda: tempfile.NamedTemporaryFile(**self._params)
|
||||
)
|
||||
self._async_file = AsyncFile(fp)
|
||||
return self._async_file
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_value: BaseException | None,
|
||||
traceback: TracebackType | None,
|
||||
) -> None:
|
||||
await self._async_file.aclose()
|
||||
|
||||
|
||||
class SpooledTemporaryFile(AsyncFile[AnyStr]):
|
||||
"""
|
||||
An asynchronous spooled temporary file that starts in memory and is spooled to disk.
|
||||
|
||||
This class provides an asynchronous interface to a spooled temporary file, much like
|
||||
Python's standard :class:`~tempfile.SpooledTemporaryFile`. It supports asynchronous
|
||||
write operations and provides a method to force a rollover to disk.
|
||||
|
||||
:param max_size: Maximum size in bytes before the file is rolled over to disk.
|
||||
:param mode: The mode in which the file is opened. Defaults to "w+b".
|
||||
:param buffering: The buffering policy (-1 means the default buffering).
|
||||
:param encoding: The encoding used to decode or encode the file (text mode only).
|
||||
:param newline: Controls how universal newlines mode works (text mode only).
|
||||
:param suffix: The suffix for the temporary file name.
|
||||
:param prefix: The prefix for the temporary file name.
|
||||
:param dir: The directory in which the temporary file is created.
|
||||
:param errors: The error handling scheme used for encoding/decoding errors.
|
||||
"""
|
||||
|
||||
_rolled: bool = False
|
||||
|
||||
@overload
|
||||
def __init__(
|
||||
self: SpooledTemporaryFile[bytes],
|
||||
max_size: int = ...,
|
||||
mode: OpenBinaryMode = ...,
|
||||
buffering: int = ...,
|
||||
encoding: str | None = ...,
|
||||
newline: str | None = ...,
|
||||
suffix: str | None = ...,
|
||||
prefix: str | None = ...,
|
||||
dir: str | None = ...,
|
||||
*,
|
||||
errors: str | None = ...,
|
||||
): ...
|
||||
@overload
|
||||
def __init__(
|
||||
self: SpooledTemporaryFile[str],
|
||||
max_size: int = ...,
|
||||
mode: OpenTextMode = ...,
|
||||
buffering: int = ...,
|
||||
encoding: str | None = ...,
|
||||
newline: str | None = ...,
|
||||
suffix: str | None = ...,
|
||||
prefix: str | None = ...,
|
||||
dir: str | None = ...,
|
||||
*,
|
||||
errors: str | None = ...,
|
||||
): ...
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
max_size: int = 0,
|
||||
mode: OpenBinaryMode | OpenTextMode = "w+b",
|
||||
buffering: int = -1,
|
||||
encoding: str | None = None,
|
||||
newline: str | None = None,
|
||||
suffix: str | None = None,
|
||||
prefix: str | None = None,
|
||||
dir: str | None = None,
|
||||
*,
|
||||
errors: str | None = None,
|
||||
) -> None:
|
||||
self._tempfile_params: dict[str, Any] = {
|
||||
"mode": mode,
|
||||
"buffering": buffering,
|
||||
"encoding": encoding,
|
||||
"newline": newline,
|
||||
"suffix": suffix,
|
||||
"prefix": prefix,
|
||||
"dir": dir,
|
||||
"errors": errors,
|
||||
}
|
||||
self._max_size = max_size
|
||||
if "b" in mode:
|
||||
super().__init__(BytesIO()) # type: ignore[arg-type]
|
||||
else:
|
||||
super().__init__(
|
||||
TextIOWrapper( # type: ignore[arg-type]
|
||||
BytesIO(),
|
||||
encoding=encoding,
|
||||
errors=errors,
|
||||
newline=newline,
|
||||
write_through=True,
|
||||
)
|
||||
)
|
||||
|
||||
async def aclose(self) -> None:
|
||||
if not self._rolled:
|
||||
self._fp.close()
|
||||
return
|
||||
|
||||
await super().aclose()
|
||||
|
||||
async def _check(self) -> None:
|
||||
if self._rolled or self._fp.tell() <= self._max_size:
|
||||
return
|
||||
|
||||
await self.rollover()
|
||||
|
||||
async def rollover(self) -> None:
|
||||
if self._rolled:
|
||||
return
|
||||
|
||||
self._rolled = True
|
||||
buffer = self._fp
|
||||
buffer.seek(0)
|
||||
self._fp = await to_thread.run_sync(
|
||||
lambda: tempfile.TemporaryFile(**self._tempfile_params)
|
||||
)
|
||||
await self.write(buffer.read())
|
||||
buffer.close()
|
||||
|
||||
@property
|
||||
def closed(self) -> bool:
|
||||
return self._fp.closed
|
||||
|
||||
async def read(self, size: int = -1) -> AnyStr:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.read(size)
|
||||
|
||||
return await super().read(size) # type: ignore[return-value]
|
||||
|
||||
async def read1(self: SpooledTemporaryFile[bytes], size: int = -1) -> bytes:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.read1(size)
|
||||
|
||||
return await super().read1(size)
|
||||
|
||||
async def readline(self) -> AnyStr:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.readline()
|
||||
|
||||
return await super().readline() # type: ignore[return-value]
|
||||
|
||||
async def readlines(self) -> list[AnyStr]:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.readlines()
|
||||
|
||||
return await super().readlines() # type: ignore[return-value]
|
||||
|
||||
async def readinto(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.readinto(b)
|
||||
|
||||
return await super().readinto(b)
|
||||
|
||||
async def readinto1(self: SpooledTemporaryFile[bytes], b: WriteableBuffer) -> int:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.readinto1(b)
|
||||
|
||||
return await super().readinto1(b)
|
||||
|
||||
async def seek(self, offset: int, whence: int | None = os.SEEK_SET) -> int:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.seek(offset, whence)
|
||||
|
||||
return await super().seek(offset, whence)
|
||||
|
||||
async def tell(self) -> int:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.tell()
|
||||
|
||||
return await super().tell()
|
||||
|
||||
async def truncate(self, size: int | None = None) -> int:
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
return self._fp.truncate(size)
|
||||
|
||||
return await super().truncate(size)
|
||||
|
||||
@overload
|
||||
async def write(self: SpooledTemporaryFile[bytes], b: ReadableBuffer) -> int: ...
|
||||
@overload
|
||||
async def write(self: SpooledTemporaryFile[str], b: str) -> int: ...
|
||||
|
||||
async def write(self, b: ReadableBuffer | str) -> int:
|
||||
"""
|
||||
Asynchronously write data to the spooled temporary file.
|
||||
|
||||
If the file has not yet been rolled over, the data is written synchronously,
|
||||
and a rollover is triggered if the size exceeds the maximum size.
|
||||
|
||||
:param s: The data to write.
|
||||
:return: The number of bytes written.
|
||||
:raises RuntimeError: If the underlying file is not initialized.
|
||||
|
||||
"""
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
result = self._fp.write(b)
|
||||
await self._check()
|
||||
return result
|
||||
|
||||
return await super().write(b) # type: ignore[misc]
|
||||
|
||||
@overload
|
||||
async def writelines(
|
||||
self: SpooledTemporaryFile[bytes], lines: Iterable[ReadableBuffer]
|
||||
) -> None: ...
|
||||
@overload
|
||||
async def writelines(
|
||||
self: SpooledTemporaryFile[str], lines: Iterable[str]
|
||||
) -> None: ...
|
||||
|
||||
async def writelines(self, lines: Iterable[str] | Iterable[ReadableBuffer]) -> None:
|
||||
"""
|
||||
Asynchronously write a list of lines to the spooled temporary file.
|
||||
|
||||
If the file has not yet been rolled over, the lines are written synchronously,
|
||||
and a rollover is triggered if the size exceeds the maximum size.
|
||||
|
||||
:param lines: An iterable of lines to write.
|
||||
:raises RuntimeError: If the underlying file is not initialized.
|
||||
|
||||
"""
|
||||
if not self._rolled:
|
||||
await checkpoint_if_cancelled()
|
||||
result = self._fp.writelines(lines)
|
||||
await self._check()
|
||||
return result
|
||||
|
||||
return await super().writelines(lines) # type: ignore[misc]
|
||||
|
||||
|
||||
class TemporaryDirectory(Generic[AnyStr]):
|
||||
"""
|
||||
An asynchronous temporary directory that is created and cleaned up automatically.
|
||||
|
||||
This class provides an asynchronous context manager for creating a temporary
|
||||
directory. It wraps Python's standard :class:`~tempfile.TemporaryDirectory` to
|
||||
perform directory creation and cleanup operations in a background thread.
|
||||
|
||||
:param suffix: Suffix to be added to the temporary directory name.
|
||||
:param prefix: Prefix to be added to the temporary directory name.
|
||||
:param dir: The parent directory where the temporary directory is created.
|
||||
:param ignore_cleanup_errors: Whether to ignore errors during cleanup
|
||||
:param delete: Whether to delete the directory upon closing (Python 3.12+).
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
suffix: AnyStr | None = None,
|
||||
prefix: AnyStr | None = None,
|
||||
dir: AnyStr | None = None,
|
||||
*,
|
||||
ignore_cleanup_errors: bool = False,
|
||||
delete: bool = True,
|
||||
) -> None:
|
||||
self.suffix: AnyStr | None = suffix
|
||||
self.prefix: AnyStr | None = prefix
|
||||
self.dir: AnyStr | None = dir
|
||||
self.ignore_cleanup_errors = ignore_cleanup_errors
|
||||
self.delete = delete
|
||||
|
||||
self._tempdir: tempfile.TemporaryDirectory | None = None
|
||||
|
||||
async def __aenter__(self) -> str:
|
||||
params: dict[str, Any] = {
|
||||
"suffix": self.suffix,
|
||||
"prefix": self.prefix,
|
||||
"dir": self.dir,
|
||||
"ignore_cleanup_errors": self.ignore_cleanup_errors,
|
||||
}
|
||||
if sys.version_info >= (3, 12):
|
||||
params["delete"] = self.delete
|
||||
|
||||
self._tempdir = await to_thread.run_sync(
|
||||
lambda: tempfile.TemporaryDirectory(**params)
|
||||
)
|
||||
return await to_thread.run_sync(self._tempdir.__enter__)
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_value: BaseException | None,
|
||||
traceback: TracebackType | None,
|
||||
) -> None:
|
||||
if self._tempdir is not None:
|
||||
with CancelScope(shield=True):
|
||||
await to_thread.run_sync(
|
||||
self._tempdir.__exit__, exc_type, exc_value, traceback
|
||||
)
|
||||
|
||||
async def cleanup(self) -> None:
|
||||
if self._tempdir is not None:
|
||||
await to_thread.run_sync(self._tempdir.cleanup)
|
||||
|
||||
|
||||
@overload
|
||||
async def mkstemp(
|
||||
suffix: str | None = None,
|
||||
prefix: str | None = None,
|
||||
dir: str | None = None,
|
||||
text: bool = False,
|
||||
) -> tuple[int, str]: ...
|
||||
|
||||
|
||||
@overload
|
||||
async def mkstemp(
|
||||
suffix: bytes | None = None,
|
||||
prefix: bytes | None = None,
|
||||
dir: bytes | None = None,
|
||||
text: bool = False,
|
||||
) -> tuple[int, bytes]: ...
|
||||
|
||||
|
||||
async def mkstemp(
|
||||
suffix: AnyStr | None = None,
|
||||
prefix: AnyStr | None = None,
|
||||
dir: AnyStr | None = None,
|
||||
text: bool = False,
|
||||
) -> tuple[int, str | bytes]:
|
||||
"""
|
||||
Asynchronously create a temporary file and return an OS-level handle and the file
|
||||
name.
|
||||
|
||||
This function wraps `tempfile.mkstemp` and executes it in a background thread.
|
||||
|
||||
:param suffix: Suffix to be added to the file name.
|
||||
:param prefix: Prefix to be added to the file name.
|
||||
:param dir: Directory in which the temporary file is created.
|
||||
:param text: Whether the file is opened in text mode.
|
||||
:return: A tuple containing the file descriptor and the file name.
|
||||
|
||||
"""
|
||||
return await to_thread.run_sync(tempfile.mkstemp, suffix, prefix, dir, text)
|
||||
|
||||
|
||||
@overload
|
||||
async def mkdtemp(
|
||||
suffix: str | None = None,
|
||||
prefix: str | None = None,
|
||||
dir: str | None = None,
|
||||
) -> str: ...
|
||||
|
||||
|
||||
@overload
|
||||
async def mkdtemp(
|
||||
suffix: bytes | None = None,
|
||||
prefix: bytes | None = None,
|
||||
dir: bytes | None = None,
|
||||
) -> bytes: ...
|
||||
|
||||
|
||||
async def mkdtemp(
|
||||
suffix: AnyStr | None = None,
|
||||
prefix: AnyStr | None = None,
|
||||
dir: AnyStr | None = None,
|
||||
) -> str | bytes:
|
||||
"""
|
||||
Asynchronously create a temporary directory and return its path.
|
||||
|
||||
This function wraps `tempfile.mkdtemp` and executes it in a background thread.
|
||||
|
||||
:param suffix: Suffix to be added to the directory name.
|
||||
:param prefix: Prefix to be added to the directory name.
|
||||
:param dir: Parent directory where the temporary directory is created.
|
||||
:return: The path of the created temporary directory.
|
||||
|
||||
"""
|
||||
return await to_thread.run_sync(tempfile.mkdtemp, suffix, prefix, dir)
|
||||
|
||||
|
||||
async def gettempdir() -> str:
|
||||
"""
|
||||
Asynchronously return the name of the directory used for temporary files.
|
||||
|
||||
This function wraps `tempfile.gettempdir` and executes it in a background thread.
|
||||
|
||||
:return: The path of the temporary directory as a string.
|
||||
|
||||
"""
|
||||
return await to_thread.run_sync(tempfile.gettempdir)
|
||||
|
||||
|
||||
async def gettempdirb() -> bytes:
|
||||
"""
|
||||
Asynchronously return the name of the directory used for temporary files in bytes.
|
||||
|
||||
This function wraps `tempfile.gettempdirb` and executes it in a background thread.
|
||||
|
||||
:return: The path of the temporary directory as bytes.
|
||||
|
||||
"""
|
||||
return await to_thread.run_sync(tempfile.gettempdirb)
|
||||
@@ -1,82 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from collections.abc import Awaitable, Generator
|
||||
from typing import Any, cast
|
||||
|
||||
from ._eventloop import get_async_backend
|
||||
|
||||
|
||||
class TaskInfo:
|
||||
"""
|
||||
Represents an asynchronous task.
|
||||
|
||||
:ivar int id: the unique identifier of the task
|
||||
:ivar parent_id: the identifier of the parent task, if any
|
||||
:vartype parent_id: Optional[int]
|
||||
:ivar str name: the description of the task (if any)
|
||||
:ivar ~collections.abc.Coroutine coro: the coroutine object of the task
|
||||
"""
|
||||
|
||||
__slots__ = "_name", "coro", "id", "name", "parent_id"
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
id: int,
|
||||
parent_id: int | None,
|
||||
name: str | None,
|
||||
coro: Generator[Any, Any, Any] | Awaitable[Any],
|
||||
):
|
||||
func = get_current_task
|
||||
self._name = f"{func.__module__}.{func.__qualname__}"
|
||||
self.id: int = id
|
||||
self.parent_id: int | None = parent_id
|
||||
self.name: str | None = name
|
||||
self.coro: Generator[Any, Any, Any] | Awaitable[Any] = coro
|
||||
|
||||
def __eq__(self, other: object) -> bool:
|
||||
if isinstance(other, TaskInfo):
|
||||
return self.id == other.id
|
||||
|
||||
return NotImplemented
|
||||
|
||||
def __hash__(self) -> int:
|
||||
return hash(self.id)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.__class__.__name__}(id={self.id!r}, name={self.name!r})"
|
||||
|
||||
def has_pending_cancellation(self) -> bool:
|
||||
"""
|
||||
Return ``True`` if the task has a cancellation pending, ``False`` otherwise.
|
||||
|
||||
"""
|
||||
return False
|
||||
|
||||
|
||||
def get_current_task() -> TaskInfo:
|
||||
"""
|
||||
Return the current task.
|
||||
|
||||
:return: a representation of the current task
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
return get_async_backend().get_current_task()
|
||||
|
||||
|
||||
def get_running_tasks() -> list[TaskInfo]:
|
||||
"""
|
||||
Return a list of running tasks in the current event loop.
|
||||
|
||||
:return: a list of task info objects
|
||||
:raises NoEventLoopError: if no supported asynchronous event loop is running in the
|
||||
current thread
|
||||
|
||||
"""
|
||||
return cast("list[TaskInfo]", get_async_backend().get_running_tasks())
|
||||
|
||||
|
||||
async def wait_all_tasks_blocked() -> None:
|
||||
"""Wait until all other tasks are waiting for something."""
|
||||
await get_async_backend().wait_all_tasks_blocked()
|
||||
@@ -1,85 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from collections.abc import Callable, Mapping
|
||||
from typing import Any, TypeVar, final, overload
|
||||
|
||||
from ._exceptions import TypedAttributeLookupError
|
||||
|
||||
if sys.version_info < (3, 15):
|
||||
from typing_extensions import sentinel
|
||||
|
||||
T_Attr = TypeVar("T_Attr")
|
||||
T_Default = TypeVar("T_Default")
|
||||
undefined = sentinel("undefined")
|
||||
|
||||
|
||||
def typed_attribute() -> Any:
|
||||
"""Return a unique object, used to mark typed attributes."""
|
||||
return object()
|
||||
|
||||
|
||||
class TypedAttributeSet:
|
||||
"""
|
||||
Superclass for typed attribute collections.
|
||||
|
||||
Checks that every public attribute of every subclass has a type annotation.
|
||||
"""
|
||||
|
||||
def __init_subclass__(cls) -> None:
|
||||
annotations: dict[str, Any] = getattr(cls, "__annotations__", {})
|
||||
for attrname in dir(cls):
|
||||
if not attrname.startswith("_") and attrname not in annotations:
|
||||
raise TypeError(
|
||||
f"Attribute {attrname!r} is missing its type annotation"
|
||||
)
|
||||
|
||||
super().__init_subclass__()
|
||||
|
||||
|
||||
class TypedAttributeProvider:
|
||||
"""Base class for classes that wish to provide typed extra attributes."""
|
||||
|
||||
@property
|
||||
def extra_attributes(self) -> Mapping[T_Attr, Callable[[], T_Attr]]:
|
||||
"""
|
||||
A mapping of the extra attributes to callables that return the corresponding
|
||||
values.
|
||||
|
||||
If the provider wraps another provider, the attributes from that wrapper should
|
||||
also be included in the returned mapping (but the wrapper may override the
|
||||
callables from the wrapped instance).
|
||||
|
||||
"""
|
||||
return {}
|
||||
|
||||
@overload
|
||||
def extra(self, attribute: T_Attr) -> T_Attr: ...
|
||||
|
||||
@overload
|
||||
def extra(self, attribute: T_Attr, default: T_Default) -> T_Attr | T_Default: ...
|
||||
|
||||
@final
|
||||
def extra(self, attribute: Any, default: object = undefined) -> object:
|
||||
"""
|
||||
extra(attribute, default=undefined)
|
||||
|
||||
Return the value of the given typed extra attribute.
|
||||
|
||||
:param attribute: the attribute (member of a :class:`~TypedAttributeSet`) to
|
||||
look for
|
||||
:param default: the value that should be returned if no value is found for the
|
||||
attribute
|
||||
:raises ~anyio.TypedAttributeLookupError: if the search failed and no default
|
||||
value was given
|
||||
|
||||
"""
|
||||
try:
|
||||
getter = self.extra_attributes[attribute]
|
||||
except KeyError:
|
||||
if default is undefined:
|
||||
raise TypedAttributeLookupError("Attribute not found") from None
|
||||
else:
|
||||
return default
|
||||
|
||||
return getter()
|
||||
@@ -1,170 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
__all__ = (
|
||||
"fix_package_names",
|
||||
"install_lazy_importer",
|
||||
"set_deprecated_aliases",
|
||||
)
|
||||
|
||||
import ast
|
||||
import inspect
|
||||
import sys
|
||||
import warnings
|
||||
from importlib import import_module
|
||||
from types import ModuleType
|
||||
from typing import Any
|
||||
|
||||
|
||||
def install_lazy_importer() -> bool:
|
||||
module_globals = sys._getframe(1).f_globals
|
||||
module_name = module_globals["__name__"]
|
||||
module_prefix = module_name + "."
|
||||
module = sys.modules[module_name]
|
||||
lazy_map, deprecated_aliases = _build_lazy_map(module)
|
||||
names = sorted(lazy_map)
|
||||
|
||||
# Delete symbols that are not part of the API
|
||||
del module_globals["TYPE_CHECKING"]
|
||||
del module_globals["install_lazy_importer"]
|
||||
|
||||
if not lazy_map and not deprecated_aliases:
|
||||
return False
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
if new_name := deprecated_aliases.get(name):
|
||||
emit_deprecation_warning(module_name, name, new_name)
|
||||
target_mod, target_attr = new_name.rsplit(".", 1)
|
||||
else:
|
||||
try:
|
||||
target_mod, target_attr = lazy_map[name]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
f"module {module_name!r} has no attribute {name!r}"
|
||||
) from None
|
||||
|
||||
imported = import_module(target_mod, module_name)
|
||||
value = getattr(imported, target_attr)
|
||||
|
||||
# patch the module name to match
|
||||
if (
|
||||
getattr(value, "__module__", "").startswith(module_prefix)
|
||||
and name not in deprecated_aliases
|
||||
):
|
||||
value.__module__ = module_name
|
||||
|
||||
module_globals[name] = value
|
||||
return value
|
||||
|
||||
def __dir__() -> list[str]:
|
||||
return names
|
||||
|
||||
module_globals["__dir__"] = __dir__
|
||||
module_globals["__getattr__"] = __getattr__
|
||||
module_globals.pop("fix_package_names", None)
|
||||
module_globals.pop("set_deprecated_aliases", None)
|
||||
return True
|
||||
|
||||
|
||||
def fix_package_names() -> None:
|
||||
module_globals = sys._getframe(1).f_globals
|
||||
module_prefix = module_globals["__name__"] + "."
|
||||
del module_globals[fix_package_names.__name__]
|
||||
for value in module_globals.values():
|
||||
if modname := getattr(value, "__module__", ""):
|
||||
if modname.startswith(module_prefix):
|
||||
parts = modname.split(".")
|
||||
value.__module__ = ".".join(
|
||||
part for part in parts if not part.startswith("_")
|
||||
)
|
||||
|
||||
|
||||
def emit_deprecation_warning(module_name: str, name: str, target: str) -> None:
|
||||
warnings.warn(
|
||||
f"The {module_name}.{name} alias is deprecated, use {target} instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
)
|
||||
|
||||
|
||||
def set_deprecated_aliases(aliases: dict[str, str]) -> None:
|
||||
module_globals = sys._getframe(1).f_globals
|
||||
module_name = module_globals["__name__"]
|
||||
del module_globals[set_deprecated_aliases.__name__]
|
||||
|
||||
def __getattr__(name: str) -> Any:
|
||||
try:
|
||||
target = aliases[name]
|
||||
except KeyError:
|
||||
raise AttributeError(
|
||||
f"module {module_name!r} has no attribute {name!r}"
|
||||
) from None
|
||||
|
||||
emit_deprecation_warning(module_name, name, target)
|
||||
target_modname, attrname = target.rsplit(".", 1)
|
||||
module = import_module(target_modname)
|
||||
return getattr(module, attrname)
|
||||
|
||||
sys.modules[module_name].__dict__["__getattr__"] = __getattr__
|
||||
|
||||
|
||||
def _build_lazy_map(
|
||||
module: ModuleType,
|
||||
) -> tuple[dict[str, tuple[str, str]], dict[str, str]]:
|
||||
try:
|
||||
source = inspect.getsource(module)
|
||||
except OSError:
|
||||
return {}, {}
|
||||
|
||||
tree = compile(source, module.__file__ or "", "exec", ast.PyCF_ONLY_AST)
|
||||
assert isinstance(tree, ast.Module)
|
||||
out: dict[str, tuple[str, str]] = {}
|
||||
deprecated_aliases: dict[str, str] = {}
|
||||
|
||||
for node in tree.body:
|
||||
if not isinstance(node, ast.If) or not _is_type_checking_block(node.test):
|
||||
continue
|
||||
|
||||
for stmt in node.body:
|
||||
match stmt:
|
||||
case ast.ImportFrom():
|
||||
base = "." * stmt.level + (stmt.module or "")
|
||||
for alias in stmt.names:
|
||||
if alias.name == "*":
|
||||
raise RuntimeError("star imports not supported")
|
||||
|
||||
exported = alias.asname or alias.name
|
||||
out[exported] = (base, alias.name)
|
||||
case ast.Expr() if isinstance(stmt.value, ast.Call):
|
||||
call = stmt.value
|
||||
if (
|
||||
isinstance(call.func, ast.Name)
|
||||
and call.func.id == "set_deprecated_aliases"
|
||||
):
|
||||
arg0 = call.args[0]
|
||||
assert isinstance(arg0, ast.Dict)
|
||||
for key, value in zip(arg0.keys, arg0.values, strict=True):
|
||||
assert isinstance(key, ast.Constant)
|
||||
assert isinstance(key.value, str)
|
||||
assert isinstance(value, ast.Constant)
|
||||
assert isinstance(value.value, str)
|
||||
deprecated_aliases[key.value] = value.value
|
||||
|
||||
return out, deprecated_aliases
|
||||
|
||||
|
||||
def _is_type_checking_block(test: ast.AST) -> bool:
|
||||
if not isinstance(test, ast.BoolOp):
|
||||
return False
|
||||
|
||||
subtest = test.values[0]
|
||||
match subtest:
|
||||
case ast.Name():
|
||||
return subtest.id == "TYPE_CHECKING"
|
||||
case ast.Attribute():
|
||||
return (
|
||||
isinstance(subtest.value, ast.Name)
|
||||
and subtest.value.id == "typing"
|
||||
and subtest.attr == "TYPE_CHECKING"
|
||||
)
|
||||
case _:
|
||||
return False
|
||||
@@ -1,63 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from .._lazyimport import (
|
||||
fix_package_names,
|
||||
install_lazy_importer,
|
||||
set_deprecated_aliases,
|
||||
)
|
||||
|
||||
if TYPE_CHECKING or not install_lazy_importer():
|
||||
from ._eventloop import AsyncBackend as AsyncBackend
|
||||
from ._resources import AsyncResource as AsyncResource
|
||||
from ._sockets import ConnectedUDPSocket as ConnectedUDPSocket
|
||||
from ._sockets import ConnectedUNIXDatagramSocket as ConnectedUNIXDatagramSocket
|
||||
from ._sockets import IPAddressType as IPAddressType
|
||||
from ._sockets import IPSockAddrType as IPSockAddrType
|
||||
from ._sockets import SocketAttribute as SocketAttribute
|
||||
from ._sockets import SocketListener as SocketListener
|
||||
from ._sockets import SocketStream as SocketStream
|
||||
from ._sockets import UDPPacketType as UDPPacketType
|
||||
from ._sockets import UDPSocket as UDPSocket
|
||||
from ._sockets import UNIXDatagramPacketType as UNIXDatagramPacketType
|
||||
from ._sockets import UNIXDatagramSocket as UNIXDatagramSocket
|
||||
from ._sockets import UNIXSocketStream as UNIXSocketStream
|
||||
from ._streams import AnyByteReceiveStream as AnyByteReceiveStream
|
||||
from ._streams import AnyByteSendStream as AnyByteSendStream
|
||||
from ._streams import AnyByteStream as AnyByteStream
|
||||
from ._streams import AnyByteStreamConnectable as AnyByteStreamConnectable
|
||||
from ._streams import (
|
||||
AnyUnreliableByteReceiveStream as AnyUnreliableByteReceiveStream,
|
||||
)
|
||||
from ._streams import AnyUnreliableByteSendStream as AnyUnreliableByteSendStream
|
||||
from ._streams import AnyUnreliableByteStream as AnyUnreliableByteStream
|
||||
from ._streams import ByteReceiveStream as ByteReceiveStream
|
||||
from ._streams import ByteSendStream as ByteSendStream
|
||||
from ._streams import ByteStream as ByteStream
|
||||
from ._streams import ByteStreamConnectable as ByteStreamConnectable
|
||||
from ._streams import Listener as Listener
|
||||
from ._streams import ObjectReceiveStream as ObjectReceiveStream
|
||||
from ._streams import ObjectSendStream as ObjectSendStream
|
||||
from ._streams import ObjectStream as ObjectStream
|
||||
from ._streams import ObjectStreamConnectable as ObjectStreamConnectable
|
||||
from ._streams import UnreliableObjectReceiveStream as UnreliableObjectReceiveStream
|
||||
from ._streams import UnreliableObjectSendStream as UnreliableObjectSendStream
|
||||
from ._streams import UnreliableObjectStream as UnreliableObjectStream
|
||||
from ._subprocesses import Process as Process
|
||||
from ._tasks import TaskGroup as TaskGroup
|
||||
from ._tasks import TaskStatus as TaskStatus
|
||||
from ._testing import TestRunner as TestRunner
|
||||
|
||||
fix_package_names()
|
||||
set_deprecated_aliases(
|
||||
{
|
||||
"CapacityLimiter": "anyio.CapacityLimiter",
|
||||
"Condition": "anyio.Condition",
|
||||
"Event": "anyio.Event",
|
||||
"Lock": "anyio.Lock",
|
||||
"Semaphore": "anyio.Semaphore",
|
||||
"CancelScope": "anyio.CancelScope",
|
||||
"BlockingPortal": "anyio.from_thread.BlockingPortal",
|
||||
}
|
||||
)
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,428 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import math
|
||||
import sys
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import (
|
||||
AsyncIterator,
|
||||
Awaitable,
|
||||
Callable,
|
||||
Coroutine,
|
||||
Iterable,
|
||||
Mapping,
|
||||
Sequence,
|
||||
)
|
||||
from contextlib import AbstractContextManager
|
||||
from os import PathLike
|
||||
from signal import Signals
|
||||
from socket import AddressFamily, SocketKind, socket
|
||||
from typing import (
|
||||
IO,
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
TypeAlias,
|
||||
TypeVar,
|
||||
overload,
|
||||
)
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import TypeVarTuple, Unpack
|
||||
else:
|
||||
from typing_extensions import TypeVarTuple, Unpack
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from _typeshed import FileDescriptorLike
|
||||
|
||||
from .._core._synchronization import CapacityLimiter, Event, Lock, Semaphore
|
||||
from .._core._tasks import CancelScope
|
||||
from .._core._testing import TaskInfo
|
||||
from ._sockets import (
|
||||
ConnectedUDPSocket,
|
||||
ConnectedUNIXDatagramSocket,
|
||||
IPSockAddrType,
|
||||
SocketListener,
|
||||
SocketStream,
|
||||
UDPSocket,
|
||||
UNIXDatagramSocket,
|
||||
UNIXSocketStream,
|
||||
)
|
||||
from ._subprocesses import Process
|
||||
from ._tasks import TaskGroup
|
||||
from ._testing import TestRunner
|
||||
|
||||
T_Retval = TypeVar("T_Retval")
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
PosArgsT = TypeVarTuple("PosArgsT")
|
||||
StrOrBytesPath: TypeAlias = str | bytes | PathLike[str] | PathLike[bytes]
|
||||
|
||||
|
||||
class AsyncBackend(metaclass=ABCMeta):
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def run(
|
||||
cls,
|
||||
func: Callable[[Unpack[PosArgsT]], Awaitable[T_Retval]],
|
||||
args: tuple[Unpack[PosArgsT]],
|
||||
kwargs: dict[str, Any],
|
||||
options: dict[str, Any],
|
||||
) -> T_Retval:
|
||||
"""
|
||||
Run the given coroutine function in an asynchronous event loop.
|
||||
|
||||
The current thread must not be already running an event loop.
|
||||
|
||||
:param func: a coroutine function
|
||||
:param args: positional arguments to ``func``
|
||||
:param kwargs: positional arguments to ``func``
|
||||
:param options: keyword arguments to call the backend ``run()`` implementation
|
||||
with
|
||||
:return: the return value of the coroutine function
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def current_token(cls) -> object:
|
||||
"""
|
||||
Return an object that allows other threads to run code inside the event loop.
|
||||
|
||||
:return: a token object, specific to the event loop running in the current
|
||||
thread
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def current_time(cls) -> float:
|
||||
"""
|
||||
Return the current value of the event loop's internal clock.
|
||||
|
||||
:return: the clock value (seconds)
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def cancelled_exception_class(cls) -> type[BaseException]:
|
||||
"""Return the exception class that is raised in a task if it's cancelled."""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def checkpoint(cls) -> None:
|
||||
"""
|
||||
Check if the task has been cancelled, and allow rescheduling of other tasks.
|
||||
|
||||
This is effectively the same as running :meth:`checkpoint_if_cancelled` and then
|
||||
:meth:`cancel_shielded_checkpoint`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def checkpoint_if_cancelled(cls) -> None:
|
||||
"""
|
||||
Check if the current task group has been cancelled.
|
||||
|
||||
This will check if the task has been cancelled, but will not allow other tasks
|
||||
to be scheduled if not.
|
||||
|
||||
"""
|
||||
if cls.current_effective_deadline() == -math.inf:
|
||||
await cls.checkpoint()
|
||||
|
||||
@classmethod
|
||||
async def cancel_shielded_checkpoint(cls) -> None:
|
||||
"""
|
||||
Allow the rescheduling of other tasks.
|
||||
|
||||
This will give other tasks the opportunity to run, but without checking if the
|
||||
current task group has been cancelled, unlike with :meth:`checkpoint`.
|
||||
|
||||
"""
|
||||
with cls.create_cancel_scope(shield=True):
|
||||
await cls.sleep(0)
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def sleep(cls, delay: float) -> None:
|
||||
"""
|
||||
Pause the current task for the specified duration.
|
||||
|
||||
:param delay: the duration, in seconds
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_cancel_scope(
|
||||
cls, *, deadline: float = math.inf, shield: bool = False
|
||||
) -> CancelScope:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def current_effective_deadline(cls) -> float:
|
||||
"""
|
||||
Return the nearest deadline among all the cancel scopes effective for the
|
||||
current task.
|
||||
|
||||
:return:
|
||||
- a clock value from the event loop's internal clock
|
||||
- ``inf`` if there is no deadline in effect
|
||||
- ``-inf`` if the current scope has been cancelled
|
||||
:rtype: float
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_task_group(cls) -> TaskGroup:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_event(cls) -> Event:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_lock(cls, *, fast_acquire: bool) -> Lock:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_semaphore(
|
||||
cls,
|
||||
initial_value: int,
|
||||
*,
|
||||
max_value: int | None = None,
|
||||
fast_acquire: bool = False,
|
||||
) -> Semaphore:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_capacity_limiter(cls, total_tokens: float) -> CapacityLimiter:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def run_sync_in_worker_thread(
|
||||
cls,
|
||||
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
||||
args: tuple[Unpack[PosArgsT]],
|
||||
abandon_on_cancel: bool = False,
|
||||
limiter: CapacityLimiter | None = None,
|
||||
) -> T_Retval:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def check_cancelled(cls) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def run_async_from_thread(
|
||||
cls,
|
||||
func: Callable[[Unpack[PosArgsT]], Coroutine[Any, Any, T_co]],
|
||||
args: tuple[Unpack[PosArgsT]],
|
||||
token: object,
|
||||
) -> T_co:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def run_sync_from_thread(
|
||||
cls,
|
||||
func: Callable[[Unpack[PosArgsT]], T_Retval],
|
||||
args: tuple[Unpack[PosArgsT]],
|
||||
token: object,
|
||||
) -> T_Retval:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def open_process(
|
||||
cls,
|
||||
command: StrOrBytesPath | Sequence[StrOrBytesPath],
|
||||
*,
|
||||
stdin: int | IO[Any] | None,
|
||||
stdout: int | IO[Any] | None,
|
||||
stderr: int | IO[Any] | None,
|
||||
cwd: StrOrBytesPath | None = None,
|
||||
env: Mapping[str, str] | None = None,
|
||||
startupinfo: Any = None,
|
||||
creationflags: int = 0,
|
||||
start_new_session: bool = False,
|
||||
pass_fds: Sequence[int] = (),
|
||||
user: str | int | None = None,
|
||||
group: str | int | None = None,
|
||||
extra_groups: Iterable[str | int] | None = None,
|
||||
umask: int = -1,
|
||||
**kwargs: Any,
|
||||
) -> Process:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def setup_process_pool_exit_at_shutdown(cls, workers: set[Process]) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def connect_tcp(
|
||||
cls, host: str, port: int, local_address: IPSockAddrType | None = None
|
||||
) -> SocketStream:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def connect_unix(cls, path: str | bytes) -> UNIXSocketStream:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_tcp_listener(cls, sock: socket) -> SocketListener:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_unix_listener(cls, sock: socket) -> SocketListener:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def create_udp_socket(
|
||||
cls,
|
||||
family: AddressFamily,
|
||||
local_address: IPSockAddrType | None,
|
||||
remote_address: IPSockAddrType | None,
|
||||
reuse_port: bool,
|
||||
) -> UDPSocket | ConnectedUDPSocket:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@overload
|
||||
async def create_unix_datagram_socket(
|
||||
cls, raw_socket: socket, remote_path: None
|
||||
) -> UNIXDatagramSocket: ...
|
||||
|
||||
@classmethod
|
||||
@overload
|
||||
async def create_unix_datagram_socket(
|
||||
cls, raw_socket: socket, remote_path: str | bytes
|
||||
) -> ConnectedUNIXDatagramSocket: ...
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def create_unix_datagram_socket(
|
||||
cls, raw_socket: socket, remote_path: str | bytes | None
|
||||
) -> UNIXDatagramSocket | ConnectedUNIXDatagramSocket:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def getaddrinfo(
|
||||
cls,
|
||||
host: bytes | str | None,
|
||||
port: str | int | None,
|
||||
*,
|
||||
family: int | AddressFamily = 0,
|
||||
type: int | SocketKind = 0,
|
||||
proto: int = 0,
|
||||
flags: int = 0,
|
||||
) -> Sequence[
|
||||
tuple[
|
||||
AddressFamily,
|
||||
SocketKind,
|
||||
int,
|
||||
str,
|
||||
tuple[str, int] | tuple[str, int, int, int] | tuple[int, bytes],
|
||||
]
|
||||
]:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def getnameinfo(
|
||||
cls, sockaddr: IPSockAddrType, flags: int = 0
|
||||
) -> tuple[str, str]:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wait_readable(cls, obj: FileDescriptorLike) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wait_writable(cls, obj: FileDescriptorLike) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def notify_closing(cls, obj: FileDescriptorLike) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_listener_socket(cls, sock: socket) -> SocketListener:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_stream_socket(cls, sock: socket) -> SocketStream:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_unix_stream_socket(cls, sock: socket) -> UNIXSocketStream:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_udp_socket(cls, sock: socket) -> UDPSocket:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_connected_udp_socket(cls, sock: socket) -> ConnectedUDPSocket:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_unix_datagram_socket(cls, sock: socket) -> UNIXDatagramSocket:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wrap_connected_unix_datagram_socket(
|
||||
cls, sock: socket
|
||||
) -> ConnectedUNIXDatagramSocket:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def current_default_thread_limiter(cls) -> CapacityLimiter:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def open_signal_receiver(
|
||||
cls, *signals: Signals
|
||||
) -> AbstractContextManager[AsyncIterator[Signals]]:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def get_current_task(cls) -> TaskInfo:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def get_running_tasks(cls) -> Sequence[TaskInfo]:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
async def wait_all_tasks_blocked(cls) -> None:
|
||||
pass
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def create_test_runner(cls, options: dict[str, Any]) -> TestRunner:
|
||||
pass
|
||||
@@ -1,39 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from types import TracebackType
|
||||
from typing import TypeVar
|
||||
|
||||
if sys.version_info >= (3, 11):
|
||||
from typing import Self
|
||||
else:
|
||||
from typing_extensions import Self
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
|
||||
class AsyncResource(metaclass=ABCMeta):
|
||||
"""
|
||||
Abstract base class for all closeable asynchronous resources.
|
||||
|
||||
Works as an asynchronous context manager which returns the instance itself on enter,
|
||||
and calls :meth:`aclose` on exit.
|
||||
"""
|
||||
|
||||
__slots__ = ()
|
||||
|
||||
async def __aenter__(self) -> Self:
|
||||
return self
|
||||
|
||||
async def __aexit__(
|
||||
self,
|
||||
exc_type: type[BaseException] | None,
|
||||
exc_val: BaseException | None,
|
||||
exc_tb: TracebackType | None,
|
||||
) -> None:
|
||||
await self.aclose()
|
||||
|
||||
@abstractmethod
|
||||
async def aclose(self) -> None:
|
||||
"""Close the resource."""
|
||||
@@ -1,401 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import errno
|
||||
import socket
|
||||
from abc import abstractmethod
|
||||
from collections.abc import Callable, Collection, Mapping
|
||||
from contextlib import AsyncExitStack
|
||||
from io import IOBase
|
||||
from ipaddress import IPv4Address, IPv6Address
|
||||
from socket import AddressFamily
|
||||
from typing import TYPE_CHECKING, Any, TypeAlias, TypeVar
|
||||
|
||||
from .._core._eventloop import get_async_backend
|
||||
from .._core._typedattr import (
|
||||
TypedAttributeProvider,
|
||||
TypedAttributeSet,
|
||||
typed_attribute,
|
||||
)
|
||||
from ._streams import ByteStream, Listener, UnreliableObjectStream
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._tasks import TaskGroup
|
||||
|
||||
IPAddressType: TypeAlias = str | IPv4Address | IPv6Address
|
||||
IPSockAddrType: TypeAlias = tuple[str, int]
|
||||
SockAddrType: TypeAlias = IPSockAddrType | str
|
||||
UDPPacketType: TypeAlias = tuple[bytes, IPSockAddrType]
|
||||
UNIXDatagramPacketType: TypeAlias = tuple[bytes, str]
|
||||
T_Retval = TypeVar("T_Retval")
|
||||
|
||||
|
||||
def _validate_socket(
|
||||
sock_or_fd: socket.socket | int,
|
||||
sock_type: socket.SocketKind,
|
||||
addr_family: socket.AddressFamily = socket.AF_UNSPEC,
|
||||
*,
|
||||
require_connected: bool = False,
|
||||
require_bound: bool = False,
|
||||
) -> socket.socket:
|
||||
if isinstance(sock_or_fd, int):
|
||||
try:
|
||||
sock = socket.socket(fileno=sock_or_fd)
|
||||
except OSError as exc:
|
||||
if exc.errno == errno.ENOTSOCK:
|
||||
raise ValueError(
|
||||
"the file descriptor does not refer to a socket"
|
||||
) from exc
|
||||
elif require_connected:
|
||||
raise ValueError("the socket must be connected") from exc
|
||||
elif require_bound:
|
||||
raise ValueError("the socket must be bound to a local address") from exc
|
||||
else:
|
||||
raise
|
||||
elif isinstance(sock_or_fd, socket.socket):
|
||||
sock = sock_or_fd
|
||||
else:
|
||||
raise TypeError(
|
||||
f"expected an int or socket, got {type(sock_or_fd).__qualname__} instead"
|
||||
)
|
||||
|
||||
try:
|
||||
if require_connected:
|
||||
try:
|
||||
sock.getpeername()
|
||||
except OSError as exc:
|
||||
raise ValueError("the socket must be connected") from exc
|
||||
|
||||
if require_bound:
|
||||
try:
|
||||
if sock.family in (socket.AF_INET, socket.AF_INET6):
|
||||
bound_addr = sock.getsockname()[1]
|
||||
else:
|
||||
bound_addr = sock.getsockname()
|
||||
except OSError:
|
||||
bound_addr = None
|
||||
|
||||
if not bound_addr:
|
||||
raise ValueError("the socket must be bound to a local address")
|
||||
|
||||
if addr_family != socket.AF_UNSPEC and sock.family != addr_family:
|
||||
raise ValueError(
|
||||
f"address family mismatch: expected {addr_family.name}, got "
|
||||
f"{sock.family.name}"
|
||||
)
|
||||
|
||||
if sock.type != sock_type:
|
||||
raise ValueError(
|
||||
f"socket type mismatch: expected {sock_type.name}, got {sock.type.name}"
|
||||
)
|
||||
except BaseException:
|
||||
# Avoid ResourceWarning from the locally constructed socket object
|
||||
if isinstance(sock_or_fd, int):
|
||||
sock.detach()
|
||||
|
||||
raise
|
||||
|
||||
sock.setblocking(False)
|
||||
return sock
|
||||
|
||||
|
||||
class SocketAttribute(TypedAttributeSet):
|
||||
"""
|
||||
.. attribute:: family
|
||||
:type: socket.AddressFamily
|
||||
|
||||
the address family of the underlying socket
|
||||
|
||||
.. attribute:: local_address
|
||||
:type: tuple[str, int] | str
|
||||
|
||||
the local address the underlying socket is connected to
|
||||
|
||||
.. attribute:: local_port
|
||||
:type: int
|
||||
|
||||
for IP based sockets, the local port the underlying socket is bound to
|
||||
|
||||
.. attribute:: raw_socket
|
||||
:type: socket.socket
|
||||
|
||||
the underlying stdlib socket object
|
||||
|
||||
.. attribute:: remote_address
|
||||
:type: tuple[str, int] | str
|
||||
|
||||
the remote address the underlying socket is connected to
|
||||
|
||||
.. attribute:: remote_port
|
||||
:type: int
|
||||
|
||||
for IP based sockets, the remote port the underlying socket is connected to
|
||||
"""
|
||||
|
||||
family: AddressFamily = typed_attribute()
|
||||
local_address: SockAddrType = typed_attribute()
|
||||
local_port: int = typed_attribute()
|
||||
raw_socket: socket.socket = typed_attribute()
|
||||
remote_address: SockAddrType = typed_attribute()
|
||||
remote_port: int = typed_attribute()
|
||||
|
||||
|
||||
class _SocketProvider(TypedAttributeProvider):
|
||||
@property
|
||||
def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
|
||||
from .._core._sockets import convert_ipv6_sockaddr as convert
|
||||
|
||||
attributes: dict[Any, Callable[[], Any]] = {
|
||||
SocketAttribute.family: lambda: self._raw_socket.family,
|
||||
SocketAttribute.local_address: lambda: convert(
|
||||
self._raw_socket.getsockname()
|
||||
),
|
||||
SocketAttribute.raw_socket: lambda: self._raw_socket,
|
||||
}
|
||||
try:
|
||||
peername: tuple[str, int] | None = convert(self._raw_socket.getpeername())
|
||||
except OSError:
|
||||
peername = None
|
||||
|
||||
# Provide the remote address for connected sockets
|
||||
if peername is not None:
|
||||
attributes[SocketAttribute.remote_address] = lambda: peername
|
||||
|
||||
# Provide local and remote ports for IP based sockets
|
||||
if self._raw_socket.family in (AddressFamily.AF_INET, AddressFamily.AF_INET6):
|
||||
attributes[SocketAttribute.local_port] = lambda: (
|
||||
self._raw_socket.getsockname()[1]
|
||||
)
|
||||
if peername is not None:
|
||||
remote_port = peername[1]
|
||||
attributes[SocketAttribute.remote_port] = lambda: remote_port
|
||||
|
||||
return attributes
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def _raw_socket(self) -> socket.socket:
|
||||
pass
|
||||
|
||||
|
||||
class SocketStream(ByteStream, _SocketProvider):
|
||||
"""
|
||||
Transports bytes over a socket.
|
||||
|
||||
Supports all relevant extra attributes from :class:`~SocketAttribute`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def from_socket(cls, sock_or_fd: socket.socket | int) -> SocketStream:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a socket stream.
|
||||
|
||||
The newly created socket wrapper takes ownership of the socket being passed in.
|
||||
The existing socket must already be connected.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a socket stream
|
||||
|
||||
"""
|
||||
sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_connected=True)
|
||||
return await get_async_backend().wrap_stream_socket(sock)
|
||||
|
||||
|
||||
class UNIXSocketStream(SocketStream):
|
||||
@classmethod
|
||||
async def from_socket(cls, sock_or_fd: socket.socket | int) -> UNIXSocketStream:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a UNIX socket stream.
|
||||
|
||||
The newly created socket wrapper takes ownership of the socket being passed in.
|
||||
The existing socket must already be connected.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a UNIX socket stream
|
||||
|
||||
"""
|
||||
sock = _validate_socket(
|
||||
sock_or_fd, socket.SOCK_STREAM, socket.AF_UNIX, require_connected=True
|
||||
)
|
||||
return await get_async_backend().wrap_unix_stream_socket(sock)
|
||||
|
||||
@abstractmethod
|
||||
async def send_fds(self, message: bytes, fds: Collection[int | IOBase]) -> None:
|
||||
"""
|
||||
Send file descriptors along with a message to the peer.
|
||||
|
||||
:param message: a non-empty bytestring
|
||||
:param fds: a collection of files (either numeric file descriptors or open file
|
||||
or socket objects)
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def receive_fds(self, msglen: int, maxfds: int) -> tuple[bytes, list[int]]:
|
||||
"""
|
||||
Receive file descriptors along with a message from the peer.
|
||||
|
||||
:param msglen: length of the message to expect from the peer
|
||||
:param maxfds: maximum number of file descriptors to expect from the peer
|
||||
:return: a tuple of (message, file descriptors)
|
||||
"""
|
||||
|
||||
|
||||
class SocketListener(Listener[SocketStream], _SocketProvider):
|
||||
"""
|
||||
Listens to incoming socket connections.
|
||||
|
||||
Supports all relevant extra attributes from :class:`~SocketAttribute`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def from_socket(
|
||||
cls,
|
||||
sock_or_fd: socket.socket | int,
|
||||
) -> SocketListener:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a socket listener.
|
||||
|
||||
The newly created listener takes ownership of the socket being passed in.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a socket listener
|
||||
|
||||
"""
|
||||
sock = _validate_socket(sock_or_fd, socket.SOCK_STREAM, require_bound=True)
|
||||
return await get_async_backend().wrap_listener_socket(sock)
|
||||
|
||||
@abstractmethod
|
||||
async def accept(self) -> SocketStream:
|
||||
"""Accept an incoming connection."""
|
||||
|
||||
async def serve(
|
||||
self,
|
||||
handler: Callable[[SocketStream], Any],
|
||||
task_group: TaskGroup | None = None,
|
||||
) -> None:
|
||||
from .. import create_task_group
|
||||
|
||||
async with AsyncExitStack() as stack:
|
||||
if task_group is None:
|
||||
task_group = await stack.enter_async_context(create_task_group())
|
||||
|
||||
while True:
|
||||
stream = await self.accept()
|
||||
task_group.start_soon(handler, stream)
|
||||
|
||||
|
||||
class UDPSocket(UnreliableObjectStream[UDPPacketType], _SocketProvider):
|
||||
"""
|
||||
Represents an unconnected UDP socket.
|
||||
|
||||
Supports all relevant extra attributes from :class:`~SocketAttribute`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def from_socket(cls, sock_or_fd: socket.socket | int) -> UDPSocket:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a UDP socket.
|
||||
|
||||
The newly created socket wrapper takes ownership of the socket being passed in.
|
||||
The existing socket must be bound to a local address.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a UDP socket
|
||||
|
||||
"""
|
||||
sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, require_bound=True)
|
||||
return await get_async_backend().wrap_udp_socket(sock)
|
||||
|
||||
async def sendto(self, data: bytes, host: str, port: int) -> None:
|
||||
"""
|
||||
Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, (host, port))).
|
||||
|
||||
"""
|
||||
return await self.send((data, (host, port)))
|
||||
|
||||
|
||||
class ConnectedUDPSocket(UnreliableObjectStream[bytes], _SocketProvider):
|
||||
"""
|
||||
Represents an connected UDP socket.
|
||||
|
||||
Supports all relevant extra attributes from :class:`~SocketAttribute`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def from_socket(cls, sock_or_fd: socket.socket | int) -> ConnectedUDPSocket:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a connected UDP socket.
|
||||
|
||||
The newly created socket wrapper takes ownership of the socket being passed in.
|
||||
The existing socket must already be connected.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a connected UDP socket
|
||||
|
||||
"""
|
||||
sock = _validate_socket(
|
||||
sock_or_fd,
|
||||
socket.SOCK_DGRAM,
|
||||
require_connected=True,
|
||||
)
|
||||
return await get_async_backend().wrap_connected_udp_socket(sock)
|
||||
|
||||
|
||||
class UNIXDatagramSocket(
|
||||
UnreliableObjectStream[UNIXDatagramPacketType], _SocketProvider
|
||||
):
|
||||
"""
|
||||
Represents an unconnected Unix datagram socket.
|
||||
|
||||
Supports all relevant extra attributes from :class:`~SocketAttribute`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def from_socket(
|
||||
cls,
|
||||
sock_or_fd: socket.socket | int,
|
||||
) -> UNIXDatagramSocket:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a UNIX datagram
|
||||
socket.
|
||||
|
||||
The newly created socket wrapper takes ownership of the socket being passed in.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a UNIX datagram socket
|
||||
|
||||
"""
|
||||
sock = _validate_socket(sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX)
|
||||
return await get_async_backend().wrap_unix_datagram_socket(sock)
|
||||
|
||||
async def sendto(self, data: bytes, path: str) -> None:
|
||||
"""Alias for :meth:`~.UnreliableObjectSendStream.send` ((data, path))."""
|
||||
return await self.send((data, path))
|
||||
|
||||
|
||||
class ConnectedUNIXDatagramSocket(UnreliableObjectStream[bytes], _SocketProvider):
|
||||
"""
|
||||
Represents a connected Unix datagram socket.
|
||||
|
||||
Supports all relevant extra attributes from :class:`~SocketAttribute`.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
async def from_socket(
|
||||
cls,
|
||||
sock_or_fd: socket.socket | int,
|
||||
) -> ConnectedUNIXDatagramSocket:
|
||||
"""
|
||||
Wrap an existing socket object or file descriptor as a connected UNIX datagram
|
||||
socket.
|
||||
|
||||
The newly created socket wrapper takes ownership of the socket being passed in.
|
||||
The existing socket must already be connected.
|
||||
|
||||
:param sock_or_fd: a socket object or file descriptor
|
||||
:return: a connected UNIX datagram socket
|
||||
|
||||
"""
|
||||
sock = _validate_socket(
|
||||
sock_or_fd, socket.SOCK_DGRAM, socket.AF_UNIX, require_connected=True
|
||||
)
|
||||
return await get_async_backend().wrap_connected_unix_datagram_socket(sock)
|
||||
@@ -1,237 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import ABCMeta, abstractmethod
|
||||
from collections.abc import Callable
|
||||
from typing import TYPE_CHECKING, Any, Generic, TypeAlias, TypeVar
|
||||
|
||||
from .._core._exceptions import EndOfStream
|
||||
from .._core._typedattr import TypedAttributeProvider
|
||||
from ._resources import AsyncResource
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._tasks import TaskGroup
|
||||
|
||||
T_Item = TypeVar("T_Item")
|
||||
T_co = TypeVar("T_co", covariant=True)
|
||||
T_contra = TypeVar("T_contra", contravariant=True)
|
||||
|
||||
|
||||
class UnreliableObjectReceiveStream(
|
||||
AsyncResource, TypedAttributeProvider, Generic[T_co]
|
||||
):
|
||||
"""
|
||||
An interface for receiving objects.
|
||||
|
||||
This interface makes no guarantees that the received messages arrive in the order in
|
||||
which they were sent, or that no messages are missed.
|
||||
|
||||
Asynchronously iterating over objects of this type will yield objects matching the
|
||||
given type parameter.
|
||||
"""
|
||||
|
||||
def __aiter__(self) -> UnreliableObjectReceiveStream[T_co]:
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> T_co:
|
||||
try:
|
||||
return await self.receive()
|
||||
except EndOfStream:
|
||||
raise StopAsyncIteration from None
|
||||
|
||||
@abstractmethod
|
||||
async def receive(self) -> T_co:
|
||||
"""
|
||||
Receive the next item.
|
||||
|
||||
:raises ~anyio.ClosedResourceError: if the receive stream has been explicitly
|
||||
closed
|
||||
:raises ~anyio.EndOfStream: if this stream has been closed from the other end
|
||||
:raises ~anyio.BrokenResourceError: if this stream has been rendered unusable
|
||||
due to external causes
|
||||
"""
|
||||
|
||||
|
||||
class UnreliableObjectSendStream(
|
||||
AsyncResource, TypedAttributeProvider, Generic[T_contra]
|
||||
):
|
||||
"""
|
||||
An interface for sending objects.
|
||||
|
||||
This interface makes no guarantees that the messages sent will reach the
|
||||
recipient(s) in the same order in which they were sent, or at all.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def send(self, item: T_contra) -> None:
|
||||
"""
|
||||
Send an item to the peer(s).
|
||||
|
||||
:param item: the item to send
|
||||
:raises ~anyio.ClosedResourceError: if the send stream has been explicitly
|
||||
closed
|
||||
:raises ~anyio.BrokenResourceError: if this stream has been rendered unusable
|
||||
due to external causes
|
||||
"""
|
||||
|
||||
|
||||
class UnreliableObjectStream(
|
||||
UnreliableObjectReceiveStream[T_Item], UnreliableObjectSendStream[T_Item]
|
||||
):
|
||||
"""
|
||||
A bidirectional message stream which does not guarantee the order or reliability of
|
||||
message delivery.
|
||||
"""
|
||||
|
||||
|
||||
class ObjectReceiveStream(UnreliableObjectReceiveStream[T_co]):
|
||||
"""
|
||||
A receive message stream which guarantees that messages are received in the same
|
||||
order in which they were sent, and that no messages are missed.
|
||||
"""
|
||||
|
||||
|
||||
class ObjectSendStream(UnreliableObjectSendStream[T_contra]):
|
||||
"""
|
||||
A send message stream which guarantees that messages are delivered in the same order
|
||||
in which they were sent, without missing any messages in the middle.
|
||||
"""
|
||||
|
||||
|
||||
class ObjectStream(
|
||||
ObjectReceiveStream[T_Item],
|
||||
ObjectSendStream[T_Item],
|
||||
UnreliableObjectStream[T_Item],
|
||||
):
|
||||
"""
|
||||
A bidirectional message stream which guarantees the order and reliability of message
|
||||
delivery.
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
async def send_eof(self) -> None:
|
||||
"""
|
||||
Send an end-of-file indication to the peer.
|
||||
|
||||
You should not try to send any further data to this stream after calling this
|
||||
method. This method is idempotent (does nothing on successive calls).
|
||||
"""
|
||||
|
||||
|
||||
class ByteReceiveStream(AsyncResource, TypedAttributeProvider):
|
||||
"""
|
||||
An interface for receiving bytes from a single peer.
|
||||
|
||||
Iterating this byte stream will yield a byte string of arbitrary length, but no more
|
||||
than 65536 bytes.
|
||||
"""
|
||||
|
||||
def __aiter__(self) -> ByteReceiveStream:
|
||||
return self
|
||||
|
||||
async def __anext__(self) -> bytes:
|
||||
try:
|
||||
return await self.receive()
|
||||
except EndOfStream:
|
||||
raise StopAsyncIteration from None
|
||||
|
||||
@abstractmethod
|
||||
async def receive(self, max_bytes: int = 65536) -> bytes:
|
||||
"""
|
||||
Receive at most ``max_bytes`` bytes from the peer.
|
||||
|
||||
.. note:: Implementers of this interface should not return an empty
|
||||
:class:`bytes` object, and users should ignore them.
|
||||
|
||||
:param max_bytes: maximum number of bytes to receive (must be a positive
|
||||
integer)
|
||||
:return: the received bytes
|
||||
:raises ValueError: if ``max_bytes`` is less than 1
|
||||
:raises ~anyio.EndOfStream: if this stream has been closed from the other end
|
||||
"""
|
||||
|
||||
|
||||
class ByteSendStream(AsyncResource, TypedAttributeProvider):
|
||||
"""An interface for sending bytes to a single peer."""
|
||||
|
||||
@abstractmethod
|
||||
async def send(self, item: bytes) -> None:
|
||||
"""
|
||||
Send the given bytes to the peer.
|
||||
|
||||
:param item: the bytes to send
|
||||
"""
|
||||
|
||||
|
||||
class ByteStream(ByteReceiveStream, ByteSendStream):
|
||||
"""A bidirectional byte stream."""
|
||||
|
||||
@abstractmethod
|
||||
async def send_eof(self) -> None:
|
||||
"""
|
||||
Send an end-of-file indication to the peer.
|
||||
|
||||
You should not try to send any further data to this stream after calling this
|
||||
method. This method is idempotent (does nothing on successive calls).
|
||||
"""
|
||||
|
||||
|
||||
#: Type alias for all unreliable bytes-oriented receive streams.
|
||||
AnyUnreliableByteReceiveStream: TypeAlias = (
|
||||
UnreliableObjectReceiveStream[bytes] | ByteReceiveStream
|
||||
)
|
||||
#: Type alias for all unreliable bytes-oriented send streams.
|
||||
AnyUnreliableByteSendStream: TypeAlias = (
|
||||
UnreliableObjectSendStream[bytes] | ByteSendStream
|
||||
)
|
||||
#: Type alias for all unreliable bytes-oriented streams.
|
||||
AnyUnreliableByteStream: TypeAlias = UnreliableObjectStream[bytes] | ByteStream
|
||||
#: Type alias for all bytes-oriented receive streams.
|
||||
AnyByteReceiveStream: TypeAlias = ObjectReceiveStream[bytes] | ByteReceiveStream
|
||||
#: Type alias for all bytes-oriented send streams.
|
||||
AnyByteSendStream: TypeAlias = ObjectSendStream[bytes] | ByteSendStream
|
||||
#: Type alias for all bytes-oriented streams.
|
||||
AnyByteStream: TypeAlias = ObjectStream[bytes] | ByteStream
|
||||
|
||||
|
||||
class Listener(AsyncResource, TypedAttributeProvider, Generic[T_co]):
|
||||
"""An interface for objects that let you accept incoming connections."""
|
||||
|
||||
@abstractmethod
|
||||
async def serve(
|
||||
self, handler: Callable[[T_co], Any], task_group: TaskGroup | None = None
|
||||
) -> None:
|
||||
"""
|
||||
Accept incoming connections as they come in and start tasks to handle them.
|
||||
|
||||
:param handler: a callable that will be used to handle each accepted connection
|
||||
:param task_group: the task group that will be used to start tasks for handling
|
||||
each accepted connection (if omitted, an ad-hoc task group will be created)
|
||||
"""
|
||||
|
||||
|
||||
class ObjectStreamConnectable(Generic[T_co], metaclass=ABCMeta):
|
||||
@abstractmethod
|
||||
async def connect(self) -> ObjectStream[T_co]:
|
||||
"""
|
||||
Connect to the remote endpoint.
|
||||
|
||||
:return: an object stream connected to the remote end
|
||||
:raises ConnectionFailed: if the connection fails
|
||||
"""
|
||||
|
||||
|
||||
class ByteStreamConnectable(metaclass=ABCMeta):
|
||||
@abstractmethod
|
||||
async def connect(self) -> ByteStream:
|
||||
"""
|
||||
Connect to the remote endpoint.
|
||||
|
||||
:return: a bytestream connected to the remote end
|
||||
:raises ConnectionFailed: if the connection fails
|
||||
"""
|
||||
|
||||
|
||||
#: Type alias for all connectables returning bytestreams or bytes-oriented object streams
|
||||
AnyByteStreamConnectable: TypeAlias = (
|
||||
ObjectStreamConnectable[bytes] | ByteStreamConnectable
|
||||
)
|
||||
@@ -1,82 +0,0 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from abc import abstractmethod
|
||||
from signal import Signals
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from ._resources import AsyncResource
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from ._streams import ByteReceiveStream, ByteSendStream
|
||||
|
||||
|
||||
class Process(AsyncResource):
|
||||
"""An asynchronous version of :class:`subprocess.Popen`."""
|
||||
|
||||
@abstractmethod
|
||||
async def wait(self) -> int:
|
||||
"""
|
||||
Wait until the process exits.
|
||||
|
||||
:return: the exit code of the process
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def terminate(self) -> None:
|
||||
"""
|
||||
Terminates the process, gracefully if possible.
|
||||
|
||||
On Windows, this calls ``TerminateProcess()``.
|
||||
On POSIX systems, this sends ``SIGTERM`` to the process.
|
||||
|
||||
.. seealso:: :meth:`subprocess.Popen.terminate`
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def kill(self) -> None:
|
||||
"""
|
||||
Kills the process.
|
||||
|
||||
On Windows, this calls ``TerminateProcess()``.
|
||||
On POSIX systems, this sends ``SIGKILL`` to the process.
|
||||
|
||||
.. seealso:: :meth:`subprocess.Popen.kill`
|
||||
"""
|
||||
|
||||
@abstractmethod
|
||||
def send_signal(self, signal: Signals) -> None:
|
||||
"""
|
||||
Send a signal to the subprocess.
|
||||
|
||||
.. seealso:: :meth:`subprocess.Popen.send_signal`
|
||||
|
||||
:param signal: the signal number (e.g. :data:`signal.SIGHUP`)
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def pid(self) -> int:
|
||||
"""The process ID of the process."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def returncode(self) -> int | None:
|
||||
"""
|
||||
The return code of the process. If the process has not yet terminated, this will
|
||||
be ``None``.
|
||||
"""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def stdin(self) -> ByteSendStream | None:
|
||||
"""The stream for the standard input of the process."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def stdout(self) -> ByteReceiveStream | None:
|
||||
"""The stream for the standard output of the process."""
|
||||
|
||||
@property
|
||||
@abstractmethod
|
||||
def stderr(self) -> ByteReceiveStream | None:
|
||||
"""The stream for the standard error output of the process."""
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user