Metadata-Version: 2.1
Name: run-markdown
Version: 1.2.68107
Summary: Extract and run code blocks from .md files
License: Unpublished
Author: Mike Orlov
Author-email: m.orlov@technokert.ru
Requires-Python: >=3.11,<4.0
Classifier: License :: Other/Proprietary License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Requires-Dist: mistune (>=3.0.2,<4.0.0)
Description-Content-Type: text/markdown

### RunMarkdown
Extract and run code blocks from .md files
### Пример проверки кода на python
Код, маркированный, как python, будет запущен с помощью python
```python 
print(2+3)
```
Следующий блок текста за исполняемым блоком будет использоваться для проверки результата  
```text
5
```
### Пример создания и последующего импорта файла на python
Если текст перед блоком кода заканчивается жирной строкой, начинающийся с `file://` и заканчивающийся на `:`, 
то код из блока будет предварительно помешён в файл(можно указывать три слеша подряд, чтобы не создавалась ссылка)  
Например, создадим **file:///summarize.py:**
```python 
def summarize(a: int, b: int) -> int:
    return a + b
```
Если после блока кода нет текстового блока, то код запускается, но проверка не выполняется  
Импортируем созданный файл
```python
from summarize import summarize
print(summarize(3, 4))
```
Проверим, что вывелось
```
7
```
Созданные файлы в текущей версии живут до окончания всего запуска, но это может измениться в дальнейшем
### Пример проверки кода, не завершающегося за 1 сек
**run_markdown** имеет таймаут на выполнение каждого блока кода.  
По дефолту это 1 секунда. Этот параметр принимается во втором аргументе запуска.  
Помимо `python` можно также запускать код на `bash`/`sh`.  
Следующий код будет 6 секунд выводить числа подряд.    
```bash
for i in {1..20}; do echo $i; sleep 0.3; done
```
По поскольку мы прождём только 1 секунду, то увидим первые 4 числа:
```
1
2
3
4
```


### Пример проверки кода, c шаблонизированным результатом
Для текстовых результатов есть механизм, 
позволяющий проверять наличие только определённых строк и/или подстрок в выводе блока
#### Шаблонизированные строки
Для шаблонизации строки есть символ три точки подряд `...`.  
Он интерпретируется, как любое количество любых символов, кроме переноса строки.
##### Пример 1
Следующий код выведет текущий год
```python
import datetime
print(datetime.datetime.now().year)
```
Допустим, мы хотим проверить первые два символа года, тогда нам подойдёт следующая запись:
```
20...
```
Она обозначает, что мы ожидаем одну строку, начинающуюся с `20` и любое количество любых символов дальше.

##### Пример 2
Похожий пример на bash
```sh
date
```
мы получим примерно такую строку: `Wed Feb 12 01:26:41 PM MSK 2025`  
Допустим, мы так же хотим проверить, что текущий год начинается с `20` 
Тогда напишем следующую проверку:
```
... 20...
``` 
Она обозначает, что мы ожидаем одну строку, начинающуюся с любого количества любых символов, потом ` 20`
и любое количество любых символов дальше.
##### Пример 3
Рассмотрим пример с несколькими строками
```python
1/0
```
Этот код выбросит исключение следующего вида:  
`Traceback (most recent call last):`  
`  File "<string>", line 1, in <module>`  
`ZeroDivisionError: division by zero`  
Допустим, нас не интересует стек, а только само исключение. Тогда мы можем написать так:
```
...
...
ZeroDivisionError: division by zero
```
Получится, что мы игнорируем дву строки, а в третьей ожидаем полного соответствия

#### Пропуск строк
В прошлом примере мы игнорировали ровно две строки, но бывает так, 
что б**о**льшую часть строк мы хотим проигнорировать, а проверить наличие меньшей.  
Или бывает, что мы не знаем точное количество строк, которое хоти проигнорировать.  
Тогда можно использовать три звёздочки подряд: `***`  
Эта последовательность символов должна быть написана на отдельной строке и тогда она обозначает,
что любое количество любых строк может быть проигнорировано.  
Рассмотрим примеры
##### Пример 1
Следующий код выведет 0-5 раз строку `hay`(сено), один раз строку `needle`(иголка) и ещё 0-5 раз `hay`
```python
import datetime
[print("hay") for _ in range(datetime.datetime.now().second // 10)]
print('needle')
[print("hay") for _ in range(datetime.datetime.now().minute // 10)]
```
Допустим, мы хотим проверить, что вывелась строка `needle`, тогда нам нужно написать следующее:
```
***
needle
***
```
Получается, что мы игнорируем любое количество строк до строки needle и любое количество после
##### Внимание
Шаблон с большим количеством тройных звёздочек, разделённых строками, при большом выводе кодового блока, 
который **НЕ соответствует** шаблону, может упереться в производительность (**зависнуть**), 
так как будет перебирать очень большое количество вариантов сопоставления большого вывода,
Например, если кодовый блок вывел
```
0
1
2
3
4
5
```
А шаблон выглядит, как
```
***
5
***
3
***
1
***
```
То вычислительная сложность грубо оценивается, как количество строк вывода **В СТЕПЕНИ** 
количества тройных звёздочек. **Таким образом при выводе не 5ти чисел, а 500ти вычислительная сложность вырастет
100000000 раз!**

### Пример проверки кода, возвращающего json
В json порядок атрибутов объектов не гарантирован, что может стать проблемой при текстовом сравнении.  
Кроме того минифицированный json (без переносов строк и пробелов) эквивалентен своей полной форме. 
```python
import json
print(json.dumps({"a": 1, "b": 2}))  # напечатает строку '{"a": 1, "b": 2}'
```
Опишем ожидаемый результат в развёрнутом виде:
Т.к. мы указали тип json, а не text, то проверка успешно прошла
```json
{
  "b": 2,
  "a": 1
}
```

### Пример проверки кода, возвращающего из python stderr
Кроме стандартного потока вывода(stdout) есть ещё и поток ошибок(stderr), они объединяются в единый вывод.
(вызов выполняется через утилиту script)
```python
import sys
print("something")
print('other', file=sys.stderr)
print('''third\nforth''', file=sys.stderr)
```
Опишем ожидаемый результат в развёрнутом виде:
```text
something
other
third
forth
```
##### Примечание
К сожалению, из-за буфферизации может меняться порядок вывода строк из stdout и stderr друг относительно друга.
Поэтому такой подход работает далеко не всегда

### Пример многострочника c кавычками на bash

### Расположение целевого файла
Для некоторых тестов может быть полезно обратиться к файлам рядом с целевым md файлом.  
Например, выведем содержимое текущего файла и проверим, что в нём есть эта строка:
```sh
cat $ORIGIN_DIR/README.md
```
Проверяем:
```text
***
...проверим, что в нём есть эта строка
***
```
