LINUX.ORG.RU

Сообщения rtxtxtrx

 

Европа задумалась о переходе на EU OS

Форум — Talks

На фоне растущих разногласий с США, включая недавние споры вокруг Гренландии и расхождения во внешней политике, Европейский Союз решил сделать шаг в сторону технологической независимости и перейти на «свою» операционку. Разработка новой операционной системы EU OS на базе Linux призвана предложить альтернативу американским решениям, таким как Windows.

( читать дальше... )

 , , ,

rtxtxtrx
()

DNS-Over-QUIC для блокировки рекламы и не только

Статьи — Администрирование
DNS-Over-QUIC для блокировки рекламы и не только

Протокол QUIC, в отличие от UDP поддерживает шифрование и работает быстрее чем TCP, который часто используется для шифрованных DNS-запросов. Данная инструкция актуальная, так как Google все-таки начал удаление поддержки блокировщиков рекламы из своего браузера. Кроме того, она пригодится тем, кто хочет скрыть свое пребывание в Интернете от тотальной слежки.

( читать дальше... )

 , , ,

rtxtxtrx
()

Переезжаем на Fish

Статьи — Администрирование
Переезжаем на Fish

Fish — это современный командный интерпретатор, недавно переписанный на Rust, который не требует особой настройки, поддерживая из коробки автодополнение и подсветку синтаксиса. Является лучшим Unix SHELL по версии Slant.

( читать дальше... )

 , ,

rtxtxtrx
()

Получаем OTP-коды для авторизации через терминал

Статьи — Администрирование
Получаем OTP-коды для авторизации через терминал

TOTP (Time-Based One-Time Password) или просто OTP — это алгоритм для генерации одноразовых паролей, который не требует сторонних сервисов. Он работает локально на основе общего секретного ключа и текущего времени. Например, GitHub с 2024 года использует двухфакторную авторизацию (ввод пароля + ввод какого-то кода) только через OTP (в России авторизация через SMS недоступна).

( читать дальше... )

 , ,

rtxtxtrx
()

Как вы относитесь к тестовым заданиям?

Голосования — Голосования
Добрый день, %YOUR_NAME%!

Пару дней назад отправляла Вам тестовую задачу на вакансию Senior JSON Developer, но не получила ответ.

Уточните, пожалуйста, планируете ли Вы выполнение тестовой задачи и когда можно ожидать результат?

Я как жесткий спамер, который 24/7 спамит все подряд, часто вижу, что HRы кидают ссылку на тестовое задание как предварительный этап собеседования даже в ООО «Рога и копыта». Я лично никогда их не делаю, но интересен процент тех, кто нарушает массовый бойкот тестовых.

>>> Результаты

 ,

rtxtxtrx
()

Все AI сломали зубы о написание парсера Brainfuck'а от Cloudflare

Форум — Development

Я просил код парсера JS сгенерировать ChatGPT, DeepSeek, Qwen и в итоге написал все сам. тут вроде все просто:

  • Из значений только пустой массив [].
  • Операторы: +, !, (, ). Приоритет должен соблюдаться.
  • Под капотом у нас строки в виде чисел, числа, булев и пустые массивы
import re


class JSExpressionParser:
    tokenizer = re.compile(r"\[\]|[+!()]")

    def __init__(self):
        self.index = 0
        self.token = None
        self.tokens = []

    def advance(self):
        try:
            self.token = self.tokens[self.index]
            self.index += 1
        except IndexError:
            self.token = None

    def parse(self, expression: str):
        self.tokens = self.tokenizer.findall(expression)
        self.index = 0
        self.advance()
        result = self.expression()
        assert self.token is None, f"unexpected token: {self.token!r}"
        return result

    def expression(self):
        left = self.factor()
        while self.token == "+":
            self.advance()
            right = self.factor()
            left = self.js_add(left, right)
        return left

    def js_add(self, left, right):
        # Если одно из значений строка, результат — строка
        # [] + 1 === '1'
        if isinstance(left, (str, list)) or isinstance(right, (str, list)):
            result = self.to_string(left) + self.to_string(right)
        else:
            result = self.to_number(left) + self.to_number(right)
        # print(f"{left!r} + {right!r} = {result!r}")
        return result

    def factor(self):
        if self.token == "+":
            self.advance()
            return self.to_number(self.factor())
        elif self.token == "!":
            self.advance()
            return not self.to_boolean(self.factor())
        elif self.token == "(":
            self.advance()
            result = self.expression()
            assert self.token == ")", (
                f"unexpected token: {self.token!r}; expected ')'"
            )
            self.advance()
            return result
        elif self.token == "[]":
            self.advance()
            return []
        else:
            raise ValueError(f"Unexpected token: {self.token}")

    def to_string(self, value):
        if value == []:
            return ""
        return str(value)

    def to_number(self, value):
        if value == []:
            return 0
        return int(value)

    def to_boolean(self, value):
        return value not in (0, False)


def parse_challenge(challenge_body: str):
    west, east = re.findall(r"(?:west|east)=([^,]+)", challenge_body)
    parser = JSExpressionParser()
    west_value = parser.parse(west)
    east_value = parser.parse(east)
    return {
        "west": west_value,
        "east": east_value,
        "wsidchk": west_value + east_value,
        "action": re.search('action="([^"]+)', challenge_body).group(1),
        "method": re.search('="([^"]+)', challenge_body).group(1),
    }


challenge_body = """\
<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="robots" content="noindex, nofollow">
    <title>One moment, please...</title>
    <!-- ... -->
<body>
    <h1>Please wait while your request is being verified...</h1>
    <form id="wsidchk-form" style="display:none;" action="/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f" method="GET">
    <input type="hidden" id="wsidchk" name="wsidchk"/>
    </form>
    <script>
    (function(){
        var west=+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])),
            east=+((+!+[])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![])+(+![]+[])),
            x=function(){try{return !!window.addEventListener;}catch(e){return !!0;} },
            y=function(y,z){x() ? document.addEventListener('DOMContentLoaded',y,z) : document.attachEvent('onreadystatechange',y);};
        y(function(){
            document.getElementById('wsidchk').value = west + east;
            document.getElementById('wsidchk-form').submit();
        }, false);
    })();
    </script>
</body>
</html>
"""

challenge = parse_challenge(challenge_body)

print(challenge)
assert challenge["west"] == 7579626
assert challenge["east"] == 15617780
assert challenge["action"] == "/z0f76a1d14fd21a8fb5fd0d03e0fdc3d3cedae52f"

Сделал я его методом тыка… Тут не все по стандартам JS, но такие выражения правильно решает:

+((+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![])+(+!+[]+!![]+[])+(+!+[]+!![]+!![]+!![]+!![]+!![]))

Хорошо… Я это написал после борьбы с придурочным AI. Решил я ему поручить все переписать на Go.

  • ChatGPT не могет, высрал говнину
  • DeepSeek сообразил сразу как правильно, но код не рабочий
  • Qwen тот же результат

Нерабочий код на Go:

package cloudflare_challenge_parser

import (
	"fmt"
	"regexp"
	"strconv"
)

// EmptyArray представляет пустой массив.
type EmptyArray struct{}

// ChallengeResult представляет результат парсинга вызова Cloudflare.
type ChallengeResult struct {
	West    int
	East    int
	Wsidchk int
	Action  string
	Method  string
}

// JSExpressionParser представляет парсер для JavaScript-выражений.
type JSExpressionParser struct {
	index  int
	token  string
	tokens []string
}

// Tokenizer для разбиения выражения на токены.
var tokenizer = regexp.MustCompile(`\[\]|[+!()]|\d+`)

// Advance переходит к следующему токену.
func (p *JSExpressionParser) Advance() {
	if p.index < len(p.tokens) {
		p.token = p.tokens[p.index]
		p.index++
	} else {
		p.token = ""
	}
}

// Parse парсит выражение и возвращает результат как целое число.
func (p *JSExpressionParser) Parse(expression string) (int, error) {
	p.tokens = tokenizer.FindAllString(expression, -1)
	p.index = 0
	p.Advance()
	result := p.expression()
	if p.token != "" {
		return 0, fmt.Errorf("unexpected token: %s", p.token)
	}
	return p.toNumber(result), nil
}

// expression обрабатывает сложение.
func (p *JSExpressionParser) expression() interface{} {
	left := p.factor()
	for p.token == "+" {
		p.Advance()
		right := p.factor()
		left = p.jsAdd(left, right)
	}
	return left
}

// jsAdd реализует логику сложения в JavaScript.
func (p *JSExpressionParser) jsAdd(left, right interface{}) interface{} {
	// Если одно из значений строка или пустой массив, результат — строка.
	if p.isStringOrEmptyArray(left) || p.isStringOrEmptyArray(right) {
		return p.toString(left) + p.toString(right)
	}
	// Иначе результат — число.
	return p.toNumber(left) + p.toNumber(right)
}

// isStringOrEmptyArray проверяет, является ли значение строкой или пустым массивом.
func (p *JSExpressionParser) isStringOrEmptyArray(value interface{}) bool {
	switch value.(type) {
	case string, EmptyArray:
		return true
	default:
		return false
	}
}

// factor обрабатывает факторы выражения.
func (p *JSExpressionParser) factor() interface{} {
	switch p.token {
	case "+":
		p.Advance()
		return p.toNumber(p.factor())
	case "!":
		p.Advance()
		return !p.toBoolean(p.factor())
	case "(":
		p.Advance()
		result := p.expression()
		if p.token != ")" {
			panic(fmt.Sprintf("unexpected token: %s; expected ')'", p.token))
		}
		p.Advance()
		return result
	case "[]":
		p.Advance()
		return EmptyArray{}
	default:
		if num, err := strconv.Atoi(p.token); err == nil {
			p.Advance()
			return num
		}
		panic(fmt.Sprintf("unexpected token: %s", p.token))
	}
}

// toBoolean преобразует значение в булево.
func (p *JSExpressionParser) toBoolean(value interface{}) bool {
	switch v := value.(type) {
	case bool:
		return v
	case int:
		return v != 0
	case string:
		return v != ""
	case EmptyArray:
		return false
	default:
		return false
	}
}

// toNumber преобразует значение в число.
func (p *JSExpressionParser) toNumber(value interface{}) int {
	switch v := value.(type) {
	case int:
		return v
	case string:
		num, err := strconv.Atoi(v)
		if err != nil {
			return 0
		}
		return num
	case EmptyArray:
		return 0
	default:
		return 0
	}
}

// toString преобразует значение в строку.
func (p *JSExpressionParser) toString(value interface{}) string {
	switch v := value.(type) {
	case string:
		return v
	case int:
		return strconv.Itoa(v)
	case EmptyArray:
		return ""
	default:
		return fmt.Sprint(value)
	}
}

// ParseChallenge парсит тело вызова Cloudflare и возвращает структуру.
func ParseChallenge(challengeBody string) (*ChallengeResult, error) {
	// Регулярные выражения для извлечения данных.
	reWestEast := regexp.MustCompile(`(?:west|east)=([^,]+)`)
	reAction := regexp.MustCompile(`action="([^"]+)"`)
	reMethod := regexp.MustCompile(`method="([^"]+)"`)

	// Извлечение значений west и east.
	matches := reWestEast.FindAllStringSubmatch(challengeBody, -1)
	if len(matches) < 2 {
		return nil, fmt.Errorf("failed to extract west and east values")
	}
	westExpr := matches[0][1]
	eastExpr := matches[1][1]

	// Парсинг west и east.
	parser := &JSExpressionParser{}
	westValue, err := parser.Parse(westExpr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse west: %v", err)
	}
	eastValue, err := parser.Parse(eastExpr)
	if err != nil {
		return nil, fmt.Errorf("failed to parse east: %v", err)
	}

	// Извлечение action и method.
	actionMatch := reAction.FindStringSubmatch(challengeBody)
	methodMatch := reMethod.FindStringSubmatch(challengeBody)
	if len(actionMatch) < 2 || len(methodMatch) < 2 {
		return nil, fmt.Errorf("failed to extract action or method")
	}
	action := actionMatch[1]
	method := methodMatch[1]

	// Формирование результата.
	return &ChallengeResult{
		West:    westValue,
		East:    eastValue,
		Wsidchk: westValue + eastValue,
		Action:  action,
		Method:  method,
	}, nil
}

Он нерабочий. Что там править, делать я в душе не знаю — тупо прикрутил интерпретатор JS, но факт, что такое я написал сам…

Итого: я все еще лучше AI…

Перемещено hobbit из talks

 

rtxtxtrx
()

Для тренировки DeepSeek-R1 используются китайские AI-чипы от Huawei

Новости — Hardware and Drivers
Для тренировки DeepSeek-R1 используются китайские AI-чипы от Huawei
Группа Hardware and Drivers

На сайте Huawei Central опубликована информация, что для тренировки нашумевшей модели DeepSeek используются AI-чипы Huawei Ascend 910C (позавчера об этом же появились сообщения в X, бывший Twitter). Изначально для обучения DeepSeek применялись Nvidia H100, причем расходы на вычислительные мощности составили около 12 миллионов долларов (по другим источникам — 6 миллионов долларов).

( читать дальше... )

>>> Официальное подтверждение

 , ,

rtxtxtrx
()

Facebook (*) начала блокировать за упоминание Linux

Новости — Безопасность
Facebook (*) начала блокировать за упоминание Linux
Группа Безопасность

Автоматический механизм модерации социальной сети Facebook (*) начал блокировать посты, в которых упоминается Linux.

Первые сообщения о блокировках появились 19 января 2025. Посты с упоминание Linux исчезли из поиска, а за ссылки на ресурсы типа distrowatch начали банить с указанием на нарушение правил, связанных с распространением вредоносного ПО. На reddit в комментариях люди отмечают, что блокировки и удаление постов с упоминанием Linux продолжаются.

По иронии судьбы сервера Facebook работают на Linux, как и недо-AI, занимающийся модерированием соцсети.

(*) принадлежит организации Meta, признана экстремистской и запрещена на территории РФ

>>> Новость на Distrowatch

 , , , ,

rtxtxtrx
()

DeepSeek выложил в открытый доступ свои модели

Новости — Open Source
DeepSeek выложил в открытый доступ свои модели
Группа Open Source

Китайская компания DeepSeek выложила в открытый доступ свои модели искусственного интеллекта, включая DeepSeek-R1 и DeepSeek-R1-Zero, что уже вызвало падение акций OpenAI на американском фондовом рынке.

( читать дальше... )

>>> Официальный репозиторий

 , , ,

rtxtxtrx
()

Поиск в Google больше не будет доступен без JavaScript

Новости — Интернет
Группа Интернет

Компания Google теперь уведомляет пользователей, что для пользования поиском нужно включить JavaScript в браузере. Ранее иностранный ресурс TechCrunch связался с сотрудниками Google для выяснения причин такого решения. Данная мера обусловлена борьбой с ботами, а также тем, что большинство пользователей пользуются поисковиком с включенным JavaScript, и многие даже не знают как его отключить. Только 0.1% пользователей поиска Google отключали его.

>>> Подробности

 ,

rtxtxtrx
()

Запущен Qwen Chat

Новости — Интернет
Группа Интернет

Команда разработчиков языковых моделей Qwen выпустила веб-версию чата, где можно опробовать их модели, которые ранее можно было скачать и использовать любой желающий, например, с сайта Qwen AI или же с Hugging Face. Там, например, есть модель с 72 миллиардами параметров. Qwen является разработкой китайской Alibaba Cloud.

( читать дальше... )

>>> Попробовать

 , , , ,

rtxtxtrx
()

Ghostty 1.0 👻

Новости — Open Source
Ghostty 1.0 👻
Группа Open Source

На днях вышла первая версия Ghostty, одного из самых быстрых эмуляторов терминала из существующих. Reddit и многие тематические каналы на YouTube уже успели окрестить его лучшим. На Github проект за короткий период собрал >15k ⭐.

( читать дальше... )

>>> Официальный сайт

 , , ,

rtxtxtrx
()

Как скрыть от собеседника окно при шаринге экрана

Форум — Desktop

Набросал скрипт https://github.com/s3rgeym/interview-helper/blob/main/main.py

Он распознает голос, и отправляет текст дегенеративной сети. Хочу алгоритмдристику проходить на какого-нить рахитектора.

Ну в общем мне понятно теперь как скрыть окно при шаринге. Я понимаю что это скорее всего возможно, но нагуглить не могу.

 

rtxtxtrx
()

Kitty не дружит с Vim: странные символы в строке статуса

Форум — Development

Поставил себе на днях это чудо и сразу вылезли артефакты в виде непонятных символов в строке статуса.

Баг с прокруткой решился элементарно:

let &t_ut=''

Потому что это решение описано в документации. А вот непонятные символы удалось победить только с помощью set t_RV=. Я, конечно, не понял причем тут видео, но перестало работать перемещение курсора кликом мыши. Тогда я модифицировал эту строку:

set t_RV= ttymouse=xterm2

Так работает вроде все, но у меня вопрос к знатокам: «А как правильно?»

В konsole все, естественно, и без этих костылей работало

" Прочие настройки
if $TERM ==# 'xterm-kitty'
  " Сброс настроек курсора, чтобы избежать артефактов при скроллинге
  " https://sw.kovidgoyal.net/kitty/faq/#using-a-color-theme-with-a-background-color-does-not-work-well-in-vim
  let &t_ut = ''
  " Какой-то мусор в tabline/statusline
  set t_RV= ttymouse=xterm2

  " Перезапуск Kitty при изменении его настроек
  autocmd BufWritePost ~/.config/kitty/kitty.conf silent !pgrep kitty && kill -SIGUSR1 $(pgrep kitty)
endif

 ,

rtxtxtrx
()

Дедупликация в Btrfs

Статьи — Администрирование
Дедупликация в Btrfs

Btrfs — это замечательная файловая система, главными фишками которой являются легковесные снапшоты, реализуемые через Copy-On-Write (COW), и сжатие. Она показала высокую надежность и стабильность, и именно поэтому она включена в ядро Linux. Однако, даже наличие COW не позволяет избежать избыточности данных на уровне блоков.

( читать дальше... )

 ,

rtxtxtrx
()

Изучение запросов на сервер от ботов

Форум — Admin

В обшем я написал такую вещь, которая среди посетителей сервака отсеивает лысых обезьян по юзер-агенту:

http {
  map $http_upgrade $connection_upgrade {
    default upgrade;
    '' close;
  }

  # Юзер-агенты
  # curl/8.10.1
  # python-requests/2.32.3
  # python-httpx/0.27.2
  # Python/3.12 aiohttp/3.9.5
  # Go-http-client/2.0

  # Фильтруем запросы от ботов
  map $http_user_agent $is_bot {
    default 0;
    ~*curl 1;
    ~*Wget 1;
    ~*python-requests 1;
    ~*python-httpx 1;
    ~*aiohttp 1;
    ~*Go-http-client 1;
  }

  # удобно с помощью jq искать нужные данные
  # TODO: добавить cookie, все заголовки
  log_format json_combined escape=json
    '{'
      '"local_time":"$time_iso8601",'
      '"ip_addr":"$remote_addr",'
      '"user_agent":"$http_user_agent",'
      '"http_cookie":"$http_cookie",'
      '"http_authorization":"$http_authorization",'
      '"referrer":"$http_referer",'
      '"content_type":"$http_content_type",'
      '"request_method":"$request_method",'
      '"request_uri":"$request_uri",'
      # '"request_url":"$scheme://$host$request_uri",'
      # '"body_bytes_sent":"$body_bytes_sent",'
      '"request_body":"$request_body",'
      '"response_status":$status,'
      '"response_content_type":"$sent_http_content_type"'
    '}';

  access_log /var/log/bot_access.log.json json_combined if=$is_bot;
  # ...
}

Что-то такое в логе имеем:

{"local_time":"2024-11-07T13:22:47+00:00","ip_addr":"45.155.91.226","user_agent":"python-requests/2.27.1","http_cookie":"","http_authorization":"Basic QURNSU46QzFzYzA=","referrer":"","content_type":"","request_method":"GET","request_uri":"/level/15/exec/-/sh/run/CR","request_body":"","response_status":404,"response_content_type":"text/html; charset=UTF-8"}

За айпи этот можете не переживать, он принадлежит говнохостингу для дорвеев из Бахрейна (dedires точка com чтобы рекламу не создавать). И в месяц он пару тысяч запросов делает. Я этих подонков не баню, хотя часто они флудят какими-то уязвимостями, которые известны с 2017 и ранее… Это скорее всего просто чей-то сайт на вротпрессе взломали через какой-нибудь плагин типа ЦЗ Super Cache…

Я запросы изучаю с целью создания сплойтов, так как детали многих уязвимостей не раскрываются либо намеренно искажаются.

И вот вопрос: а по каким юзерагентам их еще можно палить? Или есть надежнее способ?

 

rtxtxtrx
()

qBittorrent 14 лет был уязвим атаке man in the middle на HTTPS

Новости — Безопасность
qBittorrent 14  лет был уязвим атаке man in the middle на HTTPS
Группа Безопасность

В qBittorrent была закрыта 14-летняя уязвимость, связанная с некорректной проверкой SSL/TLS-сертификатов. Обновление до версии 5.0.1 устранило эту уязвтмость, которая существовала с 2010 года.

В течение этого времени программа принимала любые сертификаты, включая поддельные, что делало её уязвимой к атаке типа «человек посередине» (MitM). Это позволяло злоумышленникам незаметно изменять сетевой трафик, потенциально подвергая пользователей риску скачивания и выполнения вредоносного кода при обновлении продукта по ссылке из уведомления о выходе новой версии, а так же при загрузке бинарников Python на Windows. Уязвимость была не только теоретической, но и реализуемой на практике.

Так же отсутствие валидации сертификатов позволяло MitM подменять содержимое RSS и базы данных MaxMind Geo IP.

>>> Подробности

 , ,

rtxtxtrx
()

Делаем из Vim IDE

Статьи — Разработка
Делаем из Vim IDE

В стародавние времена когда деревья были большими, трава зеленее, а мороженное стоило по 10 копеек, на Земле жили динозавры, и эти динозавры программировали в Vim.

( читать дальше... )

 , ,

rtxtxtrx
()

Тру стори о «сдохшем» SSD

Форум — Talks

Есть у меня SATA SSD (так уж исторически сложилось, что в русском языке NVMe тоже называют SSD, и требуется уточнение) замечательной китайской фирмы Apacer. Так вот, в один момент он перестал определяться. Понял я это по долгой загрузке Arch’а. Он что-то искал по UUID 1.5 минуты. Диск этот у меня был зашифрован через LUKS и использовался для хранения моих голых фото (шучу). Открывался он при старте системы по ключу из /etc/keys. Я перепроверил /etc/crypttab. Там все было правильно. Правильными были и права на файле-ключе — 600. «Смерть!» — подумал я. Диск ничего не ответил, ведь он же диск и говорить не может… Я попробовал передернуть. Я пихал в разные дырки, но ничего не помогало. Спалил что-то на материнке, потрогав ее? Возможно, но маловероятно, и проверить я это никак не мог. Тогда я вспомнил, что в Discover долго висело предупреждение об устаревшей версии прошивки чего-то… И в один день я, по всей видимости, случайно нажал на «обновить». Discover через fwupd обновил мне прошивку диска, и тот перестал определяться… Когда это произошло, я не помню, так как компьютер у меня не выключался/перегружался неделю… «Нужен программатор!» — подумал я, а потом решил попробовать обновить прошивку на материнке… Для этого нужно просто файл кинуть на флешку. И после обновления прошивки материнки, диск начал снова определяться. Я посмотрел его SMART, там было записано всего 2 терабайта. fwupd я удалил:

yay -Rns fwupd

 , ,

rtxtxtrx
()

Настройка VS Code для работы с Python

Статьи — Разработка
Настройка VS Code для работы с Python

VS Code — это мощный инструмент для разработки на Python, который легко настроить для работы с такими полезными утилитами, как pylint, black и isort. Эти инструменты помогут поддерживать чистоту кода, единый стиль и упорядоченность импортов. Для поиска и устранения ошибок пригодится встроенный отладчик debugpy, обеспечивающий удобный процесс дебага. В дополнение ко всему можно подключить искусственного помощника для ускорения написания кода. В этой статье мы рассмотрим установку и настройку этих инструментов, а также их интеграцию с VS Code.

( читать дальше... )

 , , , ,

rtxtxtrx
()

RSS подписка на новые темы