Находки в опенсорсе
10.6K subscribers
11 photos
1 video
3 files
816 links
Привет!

Меня зовут Никита Соболев. Я занимаюсь опенсорс разработкой полный рабочий день.

Тут я рассказываю про #python, #c, опенсорс и тд.
Поддержать: https://boosty.to/sobolevn
РКН: https://vk.cc/cOzn36

Связь: @sobolev_nikita
Download Telegram
​​Funny article about memory management in #c and #rust. That's a rare combo. Don't miss it!
​​Get productive on unfamiliar source code.

Software developers spend most of their time figuring out existing source code, but common code editing tools offer little help for this task. Debuggers only allow detailed inspection of one narrow code path. 'Find all references' helps you in navigating between files, but doesn't provide the context to see the big picture of all relevant dependencies. Sourcetrail provides both overview and details by combining an interactive dependency graph, a concise code view and efficient code search, all built into an easy-to-use cross-platform developer tool. It supports you in exploring legacy code, understanding the implementation and refactoring the software architecture, making it a fun experience for the whole family!

Supported Languages: #c,#cpp, #java, #python, and possibly custom!

https://www.sourcetrail.com/
​​GoAccess is a real-time web log analyzer and interactive viewer that runs in a terminal in nix systems or through your browser.

https://github.com/allinurl/goaccess

#c
​​The pipeline #shell command!

A utility to make building up a pipeline of shell commands easier, especially when doing data exploration.

If you've ever found yourself writing shell code, in an endless loop of piping output to less, scanning it over and making changes, then pipeline can make your life just a little bit more beautiful.

This is just a thin wrapper around your shell, not some totally new data mining tool. Launch pipeline, and start typing shell commands as usual. Every time you hit enter you'll see a one-screen preview of your output, similar to piping output to less, but your cursor will stay right where it was for further editing.

https://github.com/codekitchen/pipeline

#c
​​Tig is an ncurses-based text-mode interface for git. It functions mainly as a #git repository browser, but can also assist in staging changes for commit at chunk level and act as a pager for output from various Git commands.

https://github.com/jonas/tig

#c
​​Wren is a small, fast, class-based concurrent scripting language written in #c

Think Smalltalk in a Lua-sized package with a dash of Erlang and wrapped up in a familiar, modern syntax.

- Wren is small. The VM implementation is under 4,000 semicolons. You can skim the whole thing in an afternoon. It's small, but not dense. It is readable and lovingly-commented.
- Wren is fast. A fast single-pass compiler to tight bytecode, and a compact object representation help Wren compete with other dynamic languages.
- Wren is class-based. There are lots of scripting languages out there, but many have unusual or non-existent object models. Wren places classes front and center.
- Wren is concurrent. Lightweight fibers are core to the execution model and let you organize your program into an army of communicating coroutines.
- Wren is a scripting language. Wren is intended for embedding in applications. It has no dependencies, a small standard library, and an easy-to-use C API. It compiles cleanly as C99, C++98 or anything later.

https://wren.io/
​​Lightweight static analysis for many languages. Find bug variants with patterns that look like source code.

Semgrep is a command-line tool for offline static analysis. Use pre-built or custom rules to enforce code and security standards in your codebase. You can try it now with our interactive live editor.

Semgrep combines the convenient and iterative style of grep with the powerful features of an Abstract Syntax Tree (AST) matcher and limited dataflow. Easily find function calls, class or method definitions, and more without having to understand ASTs or wrestle with regexes.

Supports #python #js #go #java and #c

https://github.com/returntocorp/semgrep
​​Miller is like awk, sed, cut, join, and sort for name-indexed data such as CSV, TSV, and tabular JSON.

With Miller, you get to use named fields without needing to count positional indices, using familiar formats such as CSV, TSV, JSON, and positionally-indexed. Then, on the fly, you can add new fields which are functions of existing fields, drop fields, sort, aggregate statistically, pretty-print, and more.

1. Miller operates on key-value-pair data while the familiar Unix tools operate on integer-indexed fields: if the natural data structure for the latter is the array, then Miller's natural data structure is the insertion-ordered hash map.
2. Miller handles a variety of data formats, including but not limited to the familiar CSV, TSV, and JSON. (Miller can handle positionally-indexed data too!)

https://github.com/johnkerl/miller

#c #go #shell
Сегодня говорим про bytes!

Вышел восьмой урок "Лучшего курса по Питону": https://www.youtube.com/watch?v=RbznhbK3vC0

Что вообще такое "Лучший курс по Питону"?
- Я решил разобрать все исходники CPython и показать, как на самом деле работают все его части
- В каждом видео я рассказываю про одну узкую тему
- Каждое видео я делю на три уровня сложности: для джунов, мидлов и сениоров
- Переодически беру интервью у других core-разработчиков CPython про разные части интерпретатора в их зоне интересов
- Получается очень хардкорно!

Например, в bytes я показываю, как устроен PyBytesObject (он простой):


typedef struct {
PyObject_VAR_HEAD
Py_DEPRECATED(3.11) Py_hash_t ob_shash;
char ob_sval[1];

/* Invariants:
* ob_sval contains space for 'ob_size+1' elements.
* ob_sval[ob_size] == 0.
* ob_shash is the hash of the byte string or -1 if not computed yet.
*/
} PyBytesObject;


Как устроен Buffer протокол для bytes с его __buffer__ и __release_buffer__:


static int
bytes_buffer_getbuffer(PyBytesObject *self, Py_buffer *view, int flags)
{
return PyBuffer_FillInfo(view, (PyObject*)self, (void *)self->ob_sval, Py_SIZE(self), 1, flags);
}

static PyBufferProcs bytes_as_buffer = {
(getbufferproc)bytes_buffer_getbuffer,
NULL,
};


Дополнительные материалы (не вошли в выпуск):
- mypy: bytes и bytearray c disable_bytearray_promotion и disable_memoryview_promotion https://github.com/python/mypy/commit/2d70ac0b33b448d5ef51c0856571068dd0754af6
- Мутабельность bytes https://docs.python.org/3.13/c-api/bytes.html#c._PyBytes_Resize
- PyBytes_Writer API: https://github.com/capi-workgroup/decisions/issues/39
- ob_shash deprecation: https://github.com/python/cpython/issues/91020

Поддержать проект можно тут: https://boosty.to/sobolevn

#лкпп #python #c
🔥5115👍1👏1
Argument Clinic

https://devguide.python.org/development-tools/clinic/

Если вы когда-нибудь смотрели исходники питона, то вы замечали внутри вот такие комментарии (взял за пример `sum()`):


/*[clinic input]
sum as builtin_sum

iterable: object
/
start: object(c_default="NULL") = 0

Return the sum of a 'start' value (default: 0) plus an iterable of numbers.
[clinic start generated code]*/

static PyObject *
builtin_sum_impl(PyObject *module, PyObject *iterable, PyObject *start)
/*[clinic end generated code: output=df758cec7d1d302f input=162b50765250d222]*/
{
// ...
}


Есть достаточно понятная проблема: нужно как-то иметь возможность передавать аргументы из Python кода в C код. Учитывая, что бывает много всяких видов Python и C функций (`METH_FASTCALL`, METH_O и тд), то все становится не так уж и просто.

AC позволяет делать достаточно просто описание сигнатуры функции при помощи специального DSL в комментариях.
И даже больше:
- Он генерирует сигнатуру сишной функции со всеми параметрами сразу после тега [clinic start generated code]
- Он хранит последнее состояние в /*[clinic end generated code: output=df758cec7d1d302f input=162b50765250d222]*/
- А еще он создает макросы вида:


PyDoc_STRVAR(builtin_sum__doc__,
"sum($module, iterable, /, start=0)\n"
"--\n"
"\n"
"Return the sum of a \'start\' value (default: 0) plus an iterable of numbers");

#define BUILTIN_SUM_METHODDEF \
{"sum", _PyCFunction_CAST(builtin_sum), METH_FASTCALL|METH_KEYWORDS, builtin_sum__doc__},


Чтобы потом использовать их для добавления методов в модули / классы:


static PyMethodDef builtin_methods[] = {
BUILTIN_SUM_METHODDEF
// ...
};


Как оно внутри?

- Есть большая либа внутри питона для работы с AC (с тестами и mypy)
- Есть make clinic для вызова данной либы на код, который вы меняете
- Можно кастомизировать выполнение либы на питоне, создавая питон код внутри C комментариев
- Мы используем AC даже для C-API тестов
- Сам генератор использует публичный C-API для выдергивания агрументов из переданных объектов. Код генерируется страшный, но читаемый, для примера кусок из файла Python/clinic/bltinmodule.c.h:


static PyObject *
builtin_sum(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
{
PyObject *return_value = NULL;
#if defined(Py_BUILD_CORE) && !defined(Py_BUILD_CORE_MODULE)

// ...

static const char * const _keywords[] = {"", "start", NULL};
static _PyArg_Parser _parser = {
.keywords = _keywords,
.fname = "sum",
.kwtuple = KWTUPLE,
};
PyObject *argsbuf[2];
Py_ssize_t noptargs = nargs + (kwnames ? PyTuple_GET_SIZE(kwnames) : 0) - 1;
PyObject *iterable;
PyObject *start = NULL;

args = _PyArg_UnpackKeywords(args, nargs, NULL, kwnames, &_parser, 1, 2, 0, argsbuf);
if (!args) {
goto exit;
}
iterable = args[0];
if (!noptargs) {
goto skip_optional_pos;
}
start = args[1];
skip_optional_pos:
return_value = builtin_sum_impl(module, iterable, start);

exit:
return return_value;
}


С ним значительно удобнее, чем писать такое руками!

---

Кстати, скоро мы с моими друзьями с Хабра делаем совместную движуху: https://vibe.habr.com/?utm_source=opensource_findings

В программе:
- Общение с разными ребятами, кто занимается карьерой
- Игра в карьерную настолку
- Специальные активности, чтобы понять, какие вайбы в работе подходят именно вам
2👍399🤡2😱1
Аллокаторы в СPython: PyArena

Один из самых простых аллокаторов в питоне. Исходники.

По сути данный аллокатор является небольшой оберткой поверх PyMem_Malloc, но с интересной особенностью. Если PyMem_Malloc имеет PyMem_Free для освобождения памяти каждого конкретного объекта, то PyArena имеет только _PyArena_Free(PyArena *arena) для освобождения сразу всей арены со всеми объектами, которые являются ее частью.

Смотрим:


struct _arena {
/* Pointer to the first block allocated for the arena, never NULL.
It is used only to find the first block when the arena is
being freed. */
block *a_head;

/* Pointer to the block currently used for allocation. Its
ab_next field should be NULL. If it is not-null after a
call to block_alloc(), it means a new block has been allocated
and a_cur should be reset to point it. */
block *a_cur;

/* A Python list object containing references to all the PyObject
pointers associated with this arena. They will be DECREFed
when the arena is freed. */
PyObject *a_objects;
};


Как мы видим, арена содержит два указателя на блоки. А вот и они:


typedef struct _block {
/* Total number of bytes owned by this block available to pass out.
Read-only after initialization. The first such byte starts at
ab_mem */
size_t ab_size;

/* Total number of bytes already passed out. The next byte available
to pass out starts at ab_mem + ab_offset */
size_t ab_offset;

/* An arena maintains a singly-linked, NULL-terminated list of
all blocks owned by the arena. These are linked via the
ab_next member */
struct _block *ab_next;

/* Pointer to the first allocatable byte owned by this block. Read-
only after initialization */
void *ab_mem;
} block;


И очищаем сразу все внутри арены:


void _PyArena_Free(PyArena *arena)
{
assert(arena);
// ...
block_free(arena->a_head);
Py_DECREF(arena->a_objects);
PyMem_Free(arena);
}


Обратите внимание, что у PyArena есть block'и и есть список обычных PyObject *. Что достигается за счет следующих АПИ:
- _PyArena_New – создает новую арену и выделяет память под нее. Создает пустой список под будущие объекты
- _PyArena_Free – очищает память существующей арены. Удаляет все блоки из памяти, декрефит объекты в списке, их собирает reference-counter
- _PyArena_Malloc – создает новый block нужного размера и сохраняет указатель на него в single-linked list
- _PyArena_AddObject – добавляет PyObject * в список отслеживаемых объектов и гарантирует, что он будет жить столько, сколько живет сама арена

Использование

Где нужна арена? На самом деле – много где. Сам подход с ареной – можно сравнить с lifetime из Rust. Все объекты внутри арены живут до одного общего конца.

Используется там, где объекты логически имеют общий lifetime. Например, при парсинге кода в AST. Ведь все дерево объектов в AST – имеет общий лайфтайм. Так намного проще обрабатывать ошибки, если произошло что-то плохое, мы просто убиваем всю арену. И нам не надо чистить все объекты в памяти ручками.

Крайне удобная штука.

Большая статья по теме: https://rfleury.com/p/untangling-lifetimes-the-arena-allocator

Выводы

Вот и single-linked list с алгособесов пригодился! 🌚️️️️
👍4235🔥10👏1😢1👌1