Homebrew (Wikipedie), správce balíčků pro macOS a od verze 2.0.0 také pro Linux, byl vydán ve verzi 4.5.0. Na stránce Homebrew Formulae lze procházet seznamem balíčků. K dispozici jsou také různé statistiky.
Byl vydán Mozilla Firefox 138.0. Přehled novinek v poznámkách k vydání a poznámkách k vydání pro vývojáře. Řešeny jsou rovněž bezpečnostní chyby. Nový Firefox 138 je již k dispozici také na Flathubu a Snapcraftu.
Šestnáctý ročník ne-konference jOpenSpace se koná 3. – 5. října 2025 v Hotelu Antoň v Telči. Pro účast je potřeba vyplnit registrační formulář. Ne-konference neznamená, že se organizátorům nechce připravovat program, ale naopak dává prostor všem pozvaným, aby si program sami složili z toho nejzajímavějšího, čím se v poslední době zabývají nebo co je oslovilo. Obsah, který vytvářejí všichni účastníci, se skládá z desetiminutových
… více »Richard Stallman přednáší ve středu 7. května od 16:30 na Technické univerzitě v Liberci o vlivu technologií na svobodu. Přednáška je určená jak odborné tak laické veřejnosti.
Jean-Baptiste Mardelle se v příspěvku na blogu rozepsal o novinkám v nejnovější verzi 25.04.0 editoru videa Kdenlive (Wikipedie). Ke stažení také na Flathubu.
TmuxAI (GitHub) je AI asistent pro práci v terminálu. Vyžaduje účet na OpenRouter.
Byla vydána nová verze R14.1.4 desktopového prostředí Trinity Desktop Environment (TDE, fork KDE 3.5, Wikipedie). Přehled novinek i s náhledy v poznámkách k vydání. Podrobný přehled v Changelogu.
Bylo vydáno OpenBSD 7.7. Opět bez písničky.
V Tiraně proběhl letošní Linux App Summit (LAS) (Mastodon). Zatím nesestříhané videozáznamy přednášek jsou k dispozici na YouTube.
Bohužel jsem však nenašel dostatek času, který bych takovým snahám mohl věnovat, a tak se k šíření povědomí o Vale vracím až nyní, kdy mě pracovní povinnosti přiměly Valu znovu oprášit a použít. Věřím, že reálné využití s reálnými výsledky lépe vystihne, proč se vyplatí o Vale uvažovat jako o programovacím jazyku některého z příštího projektů, kterými se budete věnovat.
V práci působím jako jeden z vývojářů značně populárního instalátoru GNU/Linuxové distribuce Fedora (a všeho od ní odvozeného) – Anacondy. Jedním z úkolů instalátoru je nastavení lokalizace systému (a samotné instalace), tedy jeho jazyka, rozložení klávesnice, časového pásma, fontu pro systémovou konzoli, formátu papíru apod. S tím však přichází několik algoritmicky neřešitelných problémů, které lze rozdělit do dvou kategorií: 1. Odkud vzít seznam jazyků, rozložení klávesnic, časových pásem apod., ze kterých může uživatel vybírat? 2. Jak vzájemně odvozovat informace z jedné volby pro pro volby ostatní?
Instalátor Anaconda proto ve svých zdrojových kódech obsahoval soubor s názvem lang-table, jenž obsahoval seznam jazyků spolu s mapováním na jejich zkratku, preferovaný konzolový font, tzv. locale, rozložení klávesnice a časové pásmo. Jeden řádek souboru tedy vypadal např. takto:
Czech cs latarcyrheb-sun16 cs_CZ.UTF-8 cz-lat2 Europe/Prague
Na začátku běhu instalátoru byl soubor lang-table
zpracován a uživateli byl nabídnut seznam "podporovaných" jazyků, ze kterých mohl vybírat, a další hodnoty byly odvozeny na základě tohoto výběru s tím, že např. časové pásmo bylo možné později upravit. Toto řešení však znamenalo, že přidání dalšího jazyka do výběru instalátoru znamenalo přidání řádku do lang-table
a tím změnu zdrojových kódů. Navíc byl tento soubor udržován vývojáři instalátoru, kteří ve většině případů neměli ponětí, jaké je správné výchozí rozložení klávesnice nebo časové pásmo pro který jazyk.
Bylo tedy zřejmé, že je potřeba přijít s lepším, samostatným, rozšiřitelným a snadněji udržovatelným řešením. Prvním krokem byl přechod instalátoru k zobrazování výběru jazyků na základě dostupných překladů. Jenže jak algoritmicky přejít od řetězce "cs"
k locale "cs_CZ.UTF-8"
, kde vzít řetězce "Czech"
a "čeština"
apod.? Některé z těchto problémů řešily různé kusy "magického kódu", který však v mnoha případech selhával, locales byly vyřešeny pomocí tabulky mapující na ně zkratky jazyků (označujících překlady). Tedy opět dost problémů, spousta nefungujících případů a složitá údržba. Proto vznikla iniciativa, jejíž výsledkem byly diskuze na Developer Conference 2013 v Brně a následný vznik projektu Mika Fabiana, langtable (tedy o mínus méně), který obsahuje právě taková mapování a seznamy (v podobě XML dokumentů) spolu s pythonním modulem pro dotazování. Jedná se tedy o samostatný, lépe udržovatelný, znovu využitelný a odborníky vyvíjený a udržovaný projekt, který vyřešil spoustu palčivých problémů Anacondy (částečně již u verze použité ve Fedoře 19, plně pak od Fedory 20) a do budoucna snad i jiných instalátorů, aplikací atd.
Projekt langtable pro Anacondu přinesl mnoho výhod – méně řádků zdrojových kódů, jejich lepší čitelnost, větší přesnost údajů a nahrazením modulu python-babel a objektů z něj získávaných také úsporu 20 MB v paměťové náročnosti instalátoru. Jak se říká: "So far, so good." Nicméně, jak asi již mnohé napadá, Python není zrovna ideální na zpracování velkého množství XML dat. Původní verze langtable používala modul z lxml, s jehož pomocí byl vytvořen tzv. Element Tree (v podstatě DOM strom), jenž byl procházen a z dat v něm obsažených byly tvořeny objekty ukládané v datových strukturách užívaných při dotazování. Protože však takto vytvořený strom spolu s objekty zabíral poměrně velké množství paměti (více než 30 MB pro asi 2,5 MB XML dat), bylo zpracování dat přepsáno na využití tzv. SAX parseru, který vytváří pythonní objekty přímo při průchodu XML daty. Tím bylo dosaženo úspory téměř poloviny obsazené paměti, avšak za cenu dvojnásobného navýšení času zpracování dat a tím i importu modulu langtable (např. na mém netbooku z asi 2 sekund na 4 sekundy, což už je poměrně nepřijatelná hodnota). Důvodem pro zpomalení je fakt, že zatímco lxml je napsán v Céčku, SAX parser volá funkce napsané v Pythonu. Dalším problémem je prakticky nemožnost paralelizace, neboť zpracování tří samostatných XML ve třech samostatných vláknech vede v Pythonu díky GIL
(Global Interpreter Lock) ke zpomalení, namísto očekávaného zrychlení. V neposlední řadě je zde problém s tím, že pythonní modul lze jen stěží použít z jiného prostředí nebo programovacího jazyka, než je Python.
A teď tedy, abych navnadil všechny, co už po dlouhém, Valu opomíjejícím úvodu usínají a chystají se článek zavřít, to nejdůležitější – přepsání modulu langtable do Valy, jemuž se budu věnovat ve zbytku článku, snižuje paměťovou náročnost o další 1 MB a především zrychluje import z Pythonu (zahrnující zpracování XML dat) na přibližně 0,9 sekundy! Navíc lze takto napsaný a zkompilovaný modul se dvěma malými doplňujícími soubory pro introspekci používat z velkého množství programovacích jazyků od Pythonu, přes Perl, Javu a D až po Haskell a řadu dalších.
Jak jsem již zmínil v předchozích odstavcích, není nutné pro zpracování XML dat vytvářet strom elementů, a proto i "valová" verze knihovny langtable využívá tzv. Simple API for XML, neboli SAX, konkrétně implementaci knihovny libxml2. Tato knihovna podporuje introspekci (i když nepoužívá přímo knihovnu GObject) a byl k ní vytvořen (vygenerován a doladěn) tzv. .vapi
soubor, který definuje třídy, struktury, typy apod. pro Valu a obsahuje jejich mapování na funkce, struktury apod. knihovny libxml2. Zároveň tento soubor definuje jmenný prostor (namespace
) Xml
a nepoužijeme-li na začátku našeho zdrojového kódu using Xml;
, musíme pro volání, deklarování atd. používat tento prefix. .vapi
soubor je tedy v podstatě i takovou "lehce hardcorovou" dokumentací pro používání knihovny libxml2 z Valy. Nás nejvíce zajímá struktura Xml.SAXHandler
, její atributy sloužící pro uložení odkazů na funkce reagující na události (viz dále) a její metoda (ano struktury ve Vale mohou mít metody) user_parse_file
, jenž provádí zpracování dokumentu specifikovaného jedním z argumentů.
Protože dále budu vysvětlovat, jak funguje kód pro zpracování XML dat a proč vypadá zrovna tak, jak vypadá, začnu něčím, co by se dalo nazvat "SAX v kostce" (odborníci na XML snad prominou). SAX je totiž velmi jednoduché, univerzální, ale poněkud "spartánské" API. Parser prochází XML dokument a při tom vyvolává události, na které reaguje handler. Jinými slovy, parser volá patřičné funkce handleru pro každou událost, která nastane. Pro začátek dokumentu je to funkce startDocument
, pro konec dokumentu endDocument
, obdobně pro začátek/konec elementu, characters
pro textová data (obsah elementů) a tak dále. Celkově je funkcí přes 20, ale implementace SAX většinou (libxml2 není výjimkou) umožňují definování pouze těch funkcí, které chceme provádět, a ponechání ostatních na výchozí hodnotě, což je ekvivalentní ignorování odpovídajících událostí.
Funkce definované SAX však dostávají jako argumenty velmi málo informací z XML dokumentu, např funkci startElement
je předán pouze název elementu a seznam atributů, funkci characters
jsou pak předána pouze textová data, na která parser narazil. Proto je vždy navíc předáván odkaz (void *
) na libovolnou datovou strukturu, jež je předána parseru ve funkci user_parse_file
. Programátor využívající SAX musí zajistit, aby tato datová struktura obsahovala veškeré informace nutné pro běh funkcí reagujících na události. "Jednoduchým" příkladem může být předávání odkazu na strom elementů doposud zpracované části dokumentu a jeho postupné rozšiřování. Pokud však chceme s daty z XML dokumentu pracovat jiným způsobem (např. kvůli rychlosti či paměťové náročnosti), musíme ve funkcích vytvářet úplně jiné struktury. A to je právě i případ langtable.
Chtěl bych zde předem upozornit, že jsem se držel struktury i algoritmů původní pythonní verze knihovny a neměl jsem v úmyslu je nijak optimalizovat a upravovat, neboť chci, aby se "valová" verze stala upstreamem a nadále byla vyvíjena a udržována Mikem Fabianem, autorem langtable. Zároveň si tak ukážeme, jak podobná Vala Pythonu může být. Za slušné a zdvořilé připomínky ke kódu, optimalizacím, refaktoringu apod. však budu samozřejmě vděčný.
A teď už k samotmému kódu. Knihovna langtable ukládá veškerá data ve třech interních, globálních datových strukturách odpovídajících třem XML souborům. Tyto datové struktury mapují ID elementů na objekty obsahující informace o nich. Např. struktura languages_db
obsahuje jako klíč cs
, jehož hodnotou je objekt držící kódy jazyka, názvy jazyka v různých překladech, teritoria (de facto státy) spojená s jazykem, locale hodnoty, preferované klávesnice atd. Proto handler pro zpracování souboru languages.xml
bude vytvářet právě takové objekty a přidávat je do "databáze" languages_db
.
Protože pro vytváření struktur mapujících řetězce na objekty budeme používat knihovnu Gee
, která poskytuje práci s generikami velmi podobnou např. generikám z Javy nebo C#, začíná zdrojový kód langtable.vala
(který doporučuji mít otevřený ve vedlejší záložce prohlížeče nebo oblíbeném editoru) řádkem using Gee;
, abychom se vyhnuli zbytečnému psaní prefixu Gee.
na mnoha místech. Tento jmenný prostor s ničím, co je základní součástí Valy, nekoliduje a troufnu si říci, že v podstatě tvoří jeden ze základních elementů pro programování ve Vale. Samotná knihovna langtable pak používá jmenný prostor langtable
, což je, jak si později vysvětlíme, nutné pro správné fungování introspekce např. z Pythonu a bez jmenného prostoru by nám Vala neumožnila deklarovat globální proměnné apod. V několika dalších řádcích zdrojového kódu jsou pak definovány třídy pojmenovávající instance generických typů, jež usnadňují a zpřehledňují další kód. Strukturu RankedItem
a výčtový typ LocFields
prozatím přeskočme a podívejme se na deklarace oněch tří hlavních datových struktur ("databází"). Protože mimo funkce a metody nelze vytvářet instance (Kdy by se kód provedl?), jedná se pouze o deklarace proměnných, jež budou odkazovat na instance, které později vytvoříme ve funkci (lépe řečeno procedůře) init
. Ve stejném duchu je pak deklarována proměnná pro regulární výraz, kterému se budu věnovat v části o dotazování. Číselnou konstantu (důležitou rovněž až pro dotazování) však samozřejmě můžeme plně definovat i s hodnotou.
Tím se dostáváme k části zdrojového kódu zajišťující zpracování souboru languages.xml
. Nejprve definujeme výčtový typ LanguageFields
, s jehož pomocí budeme ve funkcích handleru určovat, kam mají být ukládána data.
/***************** languages.xml parsing *****************/ private enum LanguageFields { // attributes of the LanguagesDBitem languageId, iso639_1, iso639_2_t, iso639_2_b, // fields for storing item IDs, ranks and translated names item_id, item_rank, item_name, // indicate we shouldn't store data anywhere NONE, }
Následuje definice třídy LanguagesDBitem
, což je právě ona třída udržující informace o jednom jazyce, jejíž instance se budou objevovat jako hodnoty v mapování languages_db
. Má několik atributů různých typů a jednoduchý konstruktor, který tyto atributy inicializuje.
private class LanguagesDBitem : GLib.Object { public string languageId; public string iso639_1; public string iso639_2_t; public string iso639_2_b; public NamesMap names; public StringRankMap locales; public StringRankMap territories; public StringRankMap keyboards; public StringRankMap consolefonts; public StringRankMap timezones; public LanguagesDBitem () { languageId = ""; iso639_1 = ""; iso639_2_t = ""; iso639_2_b = ""; names = new NamesMap (); locales = new StringRankMap (); territories = new StringRankMap (); keyboards = new StringRankMap (); consolefonts = new StringRankMap (); timezones = new StringRankMap (); } }
Dále je definována třída LanguagesParsingDriver
, jejíž instance je využívána jako datová struktura udržující informace o parsování a předávaná funkcím handleru. Její atribut store_to
typu LanguageFields
určuje, kam mají být ukládána data předaná funkci characters
. Atribut current_item
odkazuje na právě vytvářenou instanci LanguagesDBitem
, několik dalších atributů pak slouží pro ukládání dočasných dat. Poslední z nich, in_names
typu bool
, slouží handleru pro indikaci, zdali je parser zrovna v podstromě elementu names
či nikoliv, což je důležité, protože element languageId
může v závislosti na poloze obsahovat ID zpracovávaného jazyka nebo jazyka překladu názvu právě zpracovávaného jazyka (doporčuji nahlédnout např. na začátek souboru languages.xml
).
private class LanguageParsingDriver : GLib.Object { public LanguageFields store_to; public LanguagesDBitem curr_item; public string item_id; public string item_rank; public string item_name; public bool in_names; public LanguageParsingDriver () { store_to = LanguageFields.NONE; curr_item = null; item_id = ""; item_rank = ""; item_name = ""; in_names = false; } }
To je vše ke třídám, které budeme používat v handleru pro zpracování XML dat o jazycích a nyní se můžeme podívat na funkce, jež budou volány pro jednotlivé události. Jako první je definována funkce languageStartElement
pro reakci na začátek elementu. Ta nejprve získá instanci třídy LanguagesParsingDriver
pomocí dynamického přetypování argumentu void* data
a na základě jména začínajícího elementu upraví atributy této instance, aby bylo možné v následující volání funkce languageCharacters
(viz dále) ukládat data na správné místo.
private void languageStartElement (void* data, string name, string[] attrs) { LanguageParsingDriver driver = data as LanguageParsingDriver; switch (name) { case "language": driver.curr_item = new LanguagesDBitem (); break; case "languageId": if (!driver.in_names) driver.store_to = LanguageFields.languageId; else driver.store_to = LanguageFields.item_id; break; case "iso639-1": driver.store_to = LanguageFields.iso639_1; break; case "iso639-2-t": driver.store_to = LanguageFields.iso639_2_t; break; case "iso639-2-b": driver.store_to = LanguageFields.iso639_2_b; break; case "names": driver.in_names = true; break; case "localeId": case "keyboardId": case "territoryId": case "consolefontId": case "timezoneId": driver.store_to = LanguageFields.item_id; break; case "trName": driver.store_to = LanguageFields.item_name; break; case "rank": driver.store_to = LanguageFields.item_rank; break; } }
Velmi podobně jako funkce languagesStartElement
vypadá i funkce languagesEndElement
pro reakci na konec elementu.
private void languageEndElement (void* data, string name) { LanguageParsingDriver driver = data as LanguageParsingDriver; switch (name) { case "language": languages_db[driver.curr_item.languageId] = driver.curr_item; driver.curr_item = null; break; case "names": driver.in_names = false; break; case "name": driver.curr_item.names[driver.item_id] = driver.item_name; driver.item_id = ""; driver.item_name = ""; break; case "locale": driver.curr_item.locales[driver.item_id] = int.parse(driver.item_rank); driver.item_id = ""; driver.item_rank = ""; break; case "keyboard": driver.curr_item.keyboards[driver.item_id] = int.parse(driver.item_rank); driver.item_id = ""; driver.item_rank = ""; break; case "territory": driver.curr_item.territories[driver.item_id] = int.parse(driver.item_rank); driver.item_id = ""; driver.item_rank = ""; break; case "consolefont": driver.curr_item.consolefonts[driver.item_id] = int.parse(driver.item_rank); driver.item_id = ""; driver.item_rank = ""; break; case "timezone": driver.curr_item.timezones[driver.item_id] = int.parse(driver.item_rank); driver.item_id = ""; driver.item_rank = ""; break; } driver.store_to = LanguageFields.NONE; }
Ta získané hodnoty ukládá na správná místa – informace o jazyce do aktuální instance třídy LanguagesDBitem
a tuto instanci samotnou pak do "databáze" languages_db
. Zároveň tato funkce nastavuje pole pro ukládání dočasných dat na výchozí hodnoty (prázdné řetězce, null, false), aby nebyla míchána data jednotlivých elementů, a indikaci, kam se mají ukládat příští data (driver.store_to
) nastavuje na LanguageFields.NONE
, tedy hodnotu značící, že nejsou očekávána žádná data. Poslední v řadě funkcí handleru pro zpracování souboru langauges.xml
je languagesCharacters
, která se stará o zpracování obsahů jednotlivých elementů. SAX specifikuje, že tato funkce je volána s textovými daty obsaženými v elementu, avšak tato data mohou být předána postupně v několika voláních.
private void languageCharacters (void* data, string char_buf, int len) { LanguageParsingDriver driver = data as LanguageParsingDriver; string chars = char_buf[0:len].strip (); if (chars == "") // nothing to save return; if (driver.curr_item == null || driver.store_to == LanguageFields.NONE) // no idea where to save characters return; switch (driver.store_to) { case LanguageFields.languageId: driver.curr_item.languageId += chars; break; case LanguageFields.iso639_1: driver.curr_item.iso639_1 += chars; break; case LanguageFields.iso639_2_t: driver.curr_item.iso639_2_t += chars; break; case LanguageFields.iso639_2_b: driver.curr_item.iso639_2_b += chars; break; case LanguageFields.item_id: driver.item_id += chars; break; case LanguageFields.item_rank: driver.item_rank += chars; break; case LanguageFields.item_name: driver.item_name += chars; break; } }
V argumentech je předáván odkaz na (interní) buffer obsahující data a délka aktuálně platných dat. Proto funkce languagesCharacters
nejprve z bufferu vykopíruje data, která chce ukládat, a odstřihne prázdné znaky na začátku i konci takto získaných dat (pro případ, že by data v elementech končila novými řádky apod.). Vytváření výřezů se ve Vale chová podle toho, jestli výsledek ukládáme do unowned
nebo owned
proměnné. V prvním případě je výsledkem podřetězec vyřezávaného řetězce, ve druhém případě kopie tohoto podřetězce. Protože jsou interní atributy a proměnné bez upřesnění owned
, vytvoří výřez ve funkci languagesCharacters
kopii dat. Ta je poté ořezána, což vytvoří další kopii dat, ale díky automatické správě paměti je první (dočasná) kopie automaticky odstraněna. Dále funce pouze vezme takto upravená data a zřetězí je s daty obsaženými v poli, kdo něhož má zrovna ukládat. Proto bylo důležité tato pole ve funkci languagesEndElement
vyčistit.
To je vše, co potřebujeme pro handler reagující na události při zpracování souboru languages.xml
. Velmi podobně pak vypadají funkce pro zpracování dalších dvou souborů, liší se jen počty a názvy atributů, do nichž jsou ukládána data. Funkce pro handlery nedělají žádnou validaci XML souborů, neboť by to bylo zbytečně složité, když lze kontrolu provádět jednoduše "externě" pomocí schémat RelaxNG, která jsou k dispozici společně s XML daty. Pro zpracování souborů už zbývá jen vytvořit instance struktury SAXHandler
, nastavit její atributy startElement
, endElement
a characters
na naše funkce a zavolat metody user_parse_file
handlerů s cestami k XML souborům a instancemi "driverů".
Protože se jedná o stejný kód pro všechny soubory a protože budeme chtít data zpracovávat paralelně ve více vláknech, definujeme ještě třídu FileParser
, která v konstruktoru dostane všechny parametry potřebné pro volání user_parse_file
ve své metodě parse
. Konstruktor navíc testuje, jestli existuje verze souboru v čisté textové podobě, případně jako zkomprimovaná verze, neboť i základní rychlá komprimace XML data zmenšuje až o řád. Skvělou vlastností knihovny libxml2
je to, že zkomprimovaná data v souboru se správnou (obvyklou) příponou automaticky zpracovává pomocí dekomprimace. Proto stačí pouze zjistit, která verze je k dispozici a případně patřičně upravit cestu.
private class FileParser : GLib.Object { private Xml.SAXHandler handler; private string real_path; private void* driver; public FileParser (Xml.startElementSAXFunc start, Xml.endElementSAXFunc end, Xml.charactersSAXFunc chars, void* driver, string fpath) { handler = Xml.SAXHandler (); handler.startElement = start; handler.endElement = end; handler.characters = chars; this.driver = driver; real_path = fpath; if (!FileUtils.test (real_path, FileTest.EXISTS)) { if (FileUtils.test (real_path + ".gz", FileTest.EXISTS)) real_path += ".gz"; else real_path = ""; } } public bool parse () { if (real_path == "") return false; else handler.user_parse_file (driver, real_path); // successfully parsed return true; } }
Poslední důležitou funkcí pro tuto část je init
, jenž se stará o inicializaci knihovny, tedy především zpracování XML dat a vytvoření interních "databází", ze kterých jsou pak získávána data v dotazovacích funkcích, kterým se budeme věnovat příště. Jako první krok funkce init
vytvoří instance pro tyto databáze spolu s instancemi "driverů", jež ponesou data řídící zpracování. Dalším krokem je vytvoření instancí třídy FileParser
pro jednotlivé XML soubory a poté jsou vytvořena vlákna, která volají metody parse
takto vytvořených parserů.
public void init () { keyboards_db = new KeyboardsDB (); territories_db = new TerritoriesDB (); languages_db = new LanguagesDB (); var kb_driver = new KeyboardParsingDriver (); var ter_driver = new TerritoryParsingDriver (); var lang_driver = new LanguageParsingDriver (); var keyb_parser = new FileParser (keyboardStartElement, keyboardEndElement, keyboardCharacters, kb_driver, "/usr/share/langtable/keyboards.xml"); var ter_parser = new FileParser (territoryStartElement, territoryEndElement, territoryCharacters, ter_driver, "/usr/share/langtable/territories.xml"); var lang_parser = new FileParser (languageStartElement, languageEndElement, languageCharacters, lang_driver, "/usr/share/langtable/languages.xml"); Thread<bool> keyb_thread = new Thread<bool> ("KeybThread", keyb_parser.parse); Thread<bool> terr_thread = new Thread<bool> ("TerThread", ter_parser.parse); Thread<bool> lang_thread = new Thread<bool> ("LangThread", lang_parser.parse); locale_regex = new Regex ( // language must be 2 or 3 lower case letters: "^(?P<language>[a-z]{2,3}" + // language is only valid if "(?=$|@" + // locale string ends here or only options follow "|_[A-Z][a-z]{3}(?=$|@|_[A-Z]{2}(?=$|@))" + // valid script follows "|_[A-Z]{2}(?=$|@)" + // valid territory follows "))" + // script must be 1 upper case letter followed by // 3 lower case letters: "(?:_(?P<script>[A-Z][a-z]{3})" + // script is only valid if "(?=$|@" + // locale string ends here or only options follow "|_[A-Z]{2}(?=$|@)" + // valid territory follows ")){0,1}" + // territory must be 2 upper case letters: "(?:_(?P<territory>[A-Z]{2})" + // territory is only valid if "(?=$|@" + // locale string ends here or only options follow ")){0,1}"); if (!keyb_thread.join ()) warning ("Failed to load keyboard data!"); if (!terr_thread.join ()) warning ("Failed to load territory data!"); if (!lang_thread.join ()) warning ("Failed to load language data!"); }
Zde si dovolím dvě poznámky k práci s vlákny ve Vale:
FileParser
.Poněkud děsivý regulární výraz, který se objevuje ve funkci init
, si necháme na příště, kdy se budeme věnovat dotazovacím funkcím. Zvědavé však již teď upozorním, že tento regulární výraz byl bez změny převzat z původního zdrojového kódu pythonního modulu langtable
. V závěru inicializace pak již jen počkáme na vlákna zpracovávající XML soubory a zkontrolujeme, že zpracování proběhlo v pořádku pomocí návratových hodnot.
A to už je tedy úplně vše, co potřebujeme, abychom mohli zdrojový kód zkompilovat a pomocí volání funkce init
vyvolat zpracování dat, které ve srovnání s pythonní verzí trvá v závislosti na hardware čtvrtinu nebo i setinu času. Příště se podíváme na dotazovací funkce, s jejíchž pomocí bude možné knihovnu opravdu používat, kompilaci knihovny do podoby, ve které může být používána např. z Pythonu a několik dalších souvisejících tipů a triků.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
Diskuse byla administrátory uzamčena
značně populárního intalátoru GNU/Linuxové distribuce Fedora
Pobavilo
(a je tam překlep)
Ten nastroj na vybrani partitionu je opravdu strasny.Je doplním že je vypsaná diplomka/bakalářka na téma lepší vizualizace rozdelění disků v Anacondě. Nechce se toho někdo chopit ? :)
if (!FileUtils.test (real_path, FileTest.EXISTS)) { if (FileUtils.test (real_path + ".gz", FileTest.EXISTS)) real_path += ".gz"; else real_path = ""; }Opravdu?? Takovéhle konstrukce by v programu neměli co dělat. To je race-condition jak sviňa.
tak k chybam proste dochazi a vetsinou nema moc smysl se toho vyvarovat. V tomto pripade urcite staci smysluplne zarvat a skocit, coz zde chybi.Radostný svet užívateľov.
rozdělením na dva nástroje - jedním programem, který by onen obří XML "přežvýkal" do něčeho, co by bylo možné snadněji zpracovat
Nebo rovnou může vygenerovat kód v daném jazyce, který vytvoří patřičné objekty – a hezky propojené mezi sebou (místo aby sis někde přečetl textový řetězec a pak ho jinde hledal v nějaké mapě, tak to může být rovnou ukazatel/reference na ten objekt).
Nebo, pokud nechceme všechna data držet v paměti, může být výsledkem indexovaný binární soubor, ve kterém se bude snadno vyhledávat.
A jak tam zapíšeš ty reference? (abys toho při tvorbě dat musel napsat minimum, ale zároveň to bylo propojené, aby při běhu nebylo potřeba prohledávat nějaké mapy podle textových klíčů, ale měl jsi hned odkaz na další objekt)
elif name == u"name":
self._names[str(self._item_id)] = self._item_name
self._clear_item()
elif name == u"locale":
self._locales[str(self._item_id)] = int(self._item_rank)
self._clear_item()
elif name == u"territory":
self._territories[str(self._item_id)] = int(self._item_rank)
self._clear_item()
elif name == u"keyboard":
self._keyboards[str(self._item_id)] = int(self._item_rank)
self._clear_item()
elif name == u"consolefont":
self._consolefonts[str(self._item_id)] = int(self._item_rank)
self._clear_item()
elif name == u"timezone":
self._timezones[str(self._item_id)] = int(self._item_rank)
self._clear_item()
Toto je jasne opakovani se porad dokola, a to neni jediny pripad ...
Z toho co jsem videl, si myslim ze toto psal nekdo kdo je primarne C++/Vala programator, kde se programuje timto stylem, proto si dovolim rici, ze pokud chcete urychlit svuj program, tak by to chtelo vic abstrakci, a rozhodne mene radku kodu, Python script s 1300+ radky je spatne. Toto ma byt rozdelene do nekolika (mozna desitek) mensich scriptu v ramci jednoho modulu, nebo pouzivat vice standartnich knihoven ...
Prectete si ZEN Pythonu ....
Stejne tak se mi nelibi neco jako
if neco == None:
return
Znovu, ZEN Pythonu, explicit is better than implicit, zde ma byt return None, nebo False ... zkratka neco ..
Snazite si setrit si pismena na spatnych mistech ...
Nerikam ze jste spatni programatori, tak na me prosim nekricte, ale jelikoz ja delam v Pythonu kazdy den a vidivam casto Python kod od Cckaru, poznam jak to vypada ...
Z nejakeho duvodu maji Cckari tendenci psat strasne kodu .. :D
Mozna kdyby to napsal nekdo kdo dela Python uz par let, bylo by to rychlejsi, stejne tak jak kluci psali, zmenit XML na neco jineho je taky dobrej napad ..
A jeste poznamka k tvemu GIL v Pythonu, proc bys na cteni vicero souboru nemohl pouzit multiprocessing?? (vice ram, ja vim, ale jde to optimalizovat)
Cau
$ time python /dev/null real 0m0.014s user 0m0.010s sys 0m0.003sNa Atom D510 (CentOS 6.4):
$ time python /dev/null real 0m0.062s user 0m0.034s sys 0m0.020sTakze tu muzeme vsichni hazet cislama, jak chceme :)
$ time python /dev/null real 0m0.027s user 0m0.008s sys 0m0.012s $ time python3 /dev/null real 0m0.047s user 0m0.040s sys 0m0.004score duo 2 (6 GB ram) python3 mi 50 mS trvá taky, samozřejmě po opětovném spouštění, ať se python dostane z disku do cache.
$time python3 /dev/null real 0m0.121s user 0m0.107s sys 0m0.013s $time python2 /dev/null real 0m0.183s user 0m0.163s sys 0m0.013sNa Athlon X2 5200+ 2.6GHz, podtaktovaný na 1GHz
$time python3 /dev/null real 0m0.072s user 0m0.030s sys 0m0.003s $time python2 /dev/null real 0m0.028s user 0m0.017s sys 0m0.007s
[~]$ time python -c '' real 0m0.027s user 0m0.024s sys 0m0.003s [~]$ time perl -e '' real 0m0.004s user 0m0.001s sys 0m0.003s [~]$ time lua -e '' real 0m0.002s user 0m0.000s sys 0m0.001s
na skriptování (to volat to z něčeho jiného) to použitelné není27 milisekund není tak špatné, pokud to člověk nebude volat někde ve smyčce, tak mi to přijde docela použitelné.
python -S -c ''
? Prípadne by sa dal ešte zneužiť fork.
switch
.
Python není zrovna ideální na zpracování velkého množství XML dat
no můj názor je opačný, že na tohle je vhodný skriptovací jazyk s příslušnou knihovnou (např. napsanou v C) "téměř" ideální
ten regex by šel napsat (no zabralo mi nějaký čas, než jsem přišel na to, čemu vlastně má odpovídat)
my $local_regex = qr{ ^ #zacatek stringu raději bych použil \A (?P<language> [a-z]{2,3} #locale (?: _ (?P<script> [A-Z][a-z]{3} #script (optional) ) )? (?: _ (?P<territory> [A-Z]{2} #territory (optional) ) )? (?: #místo nasl 4 řádků bych mohl použít i to vaše (?: @ | $ ) @.* #options (optional) )? $ #konec stringu raději \z ) }xms;nebo ještě lépe
my $under_line = q{_}; my $at_sign = q{@}; my $locale_rs = '[a-z]{2,3}'; my $script_rs = '(?P<script> [A-Z][a-z]{3} )'; my $territory_rs = '(?P<territory> [A-Z]{2} )'; my $options_rs = q{.*}; my $local_re = qr{ \A (?P<language> $locale_rs (?: $under_line $script_rs )? (?: $under_line $territory_rs )? (?: $at_sign $options_rs )? ) \z }xms;PS. psáno pro Perl pro Valu si to prosím upravte
V první řadě díky za pokračování seriálu. Valu jsem zatím k ničemu nepoužil, ale už po ní zálibně koukám celkem dlouho
řádkem using Gee;, abychom se vyhnuli zbytečnému psaní prefixu Gee. na mnoha místech. Tento jmenný prostor s ničím, co je základní součástí Valy, nekoliduje
Lepší je importovat jen ty třídy, které používám, ne celý jmenný prostor. Alespoň tak to dělám v Javě.
Ta získané hodnoty ukládá na správná místa – informace o jazyce do aktuální instance třídy LanguagesDBitem a tuto instanci samotnou pak do "databáze" languages_db. Zároveň tato funkce nastavuje pole pro ukládání dočasných dat na výchozí hodnoty (prázdné řetězce, null, false), aby nebyla míchána data jednotlivých elementů, a indikaci, kam se mají ukládat příští data (driver.store_to) nastavuje na LanguageFields.NONE, tedy hodnotu značící, že nejsou očekávána žádná data.
SAX je určitě zlepšení, ale přijde mi to pořád hodně nízkoúrovňové. Mnohem elegantnější by bylo použít nějaké mapování z XML na objekty. Jak by to vypadalo v Javě: LangTable. Ale zase to (mapování) nemusí být optimální z hlediska výkonu – pak je otázka, jestli mít hezký stručný kód (pár řádků, holé struktury s pár anotacemi, žádné cykly ani switche) nebo výkonově optimalizovaný kód se spoustou ručně psaného balastu. Parsování keyboards.xml
na mém počítači zabralo 114 ms.
BTW: nechcete někdo tenhle kousek programu napsat v jiném jazyce, který by umožnil jednoduché mapování (deklarativní, bez zbytečného ručně psaného kódu) a zároveň by byl rychlejší než ta Java?
Jak tak koukám do těch datových souborů, jsou to struktury, kde všechno souvisí se vším. To se v XML sice namodelovat dá a pokud napíšeš slušně schéma, dá se v tom i udržet referenční integrita, ale není to úplně ono.1 Relační databáze by asi byla přirozenější a vhodnější než XML2. Navíc bys ani nemusel řešit to mapování na objekty – pomocí jednoduchého SQL dotazu by sis vytáhl přesně to, co potřebuješ. Jedinou nevýhodu vidím v horším verzování – chce to verzovat SQL skript místo binárních databázových souborů, takže je to krok navíc při kompilaci (ale to zas taková tragédie není).
[1] navíc současný stav (tři soubory) není dokonalý, přijde mi, že tam bude spousta duplicit a příležitostí pro vznik nekonzistence
[2] nebo jakýkoli podobný souborový formát – ne, INI, JSON, YAML atd. skutečně není řešením
Relační databáze by asi byla přirozenější a vhodnější než XML2. Navíc bys ani nemusel řešit to mapování na objekty – pomocí jednoduchého SQL dotazu by sis vytáhl přesně to, co potřebuješ. Jedinou nevýhodu vidím v horším verzování – chce to verzovat SQL skript místo binárních databázových souborů, takže je to krok navíc při kompilaci (ale to zas taková tragédie není)To je tak neunixove reseni az to boli.
Relační databáze by asi byla přirozenější a vhodnější než XML nebo jakýkoli podobný souborový formát – ne, INI, JSON, YAML atd. skutečně není řešením.A co tak pouzit vlastni jednoduchy DSL a parser si nechat vygenerovat treba Antlr?
To je tak neunixove reseni az to boli.
SQL skript je přece krásný textový konfigurák s jasnou syntaxí
Pak se akorát parsuje pomocí již hotového nástroje, uloží se efektivním způsobem a dá se k němu přistupovat pomocí určitého existujícího API.
A co tak pouzit vlastni jednoduchy DSL a parser si nechat vygenerovat treba Antlr?
To je taky možnost, ale nebude to pak další z mnoha různých syntaxí, které se uživatel musí učit? DSL mi přijde vhodné pro úlohy, které jsou sice specifické (obecný jazyk pro ně není ideální), ale přesto opakovatelné (používají se na víc místech, ve víc programech).
Šel bych na to asi buď cestou relační databáze (pokud se hodí silný dotazovací jazyk) nebo cestou předžvýkání – buď do binárního souboru (pokud to chceme mít na disku) nebo do kódu daného jazyka (pokud můžeme mít všechno v paměti).
SQL skript je přece krásný textový konfigurák s jasnou syntaxíNejsem si prilis jisty, jestli to myslis vazne nebo jako sverazny humor. Uz jsem videl ledacos...
To je taky možnost, ale nebude to pak další z mnoha různých syntaxí, které se uživatel musí učit?Naucit se novy format je intelektualne minimalne stejne tak narocne jako naucit se schema XML souboru.
Šel bych na to asi buď cestou relační databáze (pokud se hodí silný dotazovací jazyk) nebo cestou předžvýkání – buď do binárního souboru (pokud to chceme mít na disku) nebo do kódu daného jazyka (pokud můžeme mít všechno v paměti).V takovem pripade budes muset spravovat krome programu samotneho jeste dalsi nastroje na spravu jeho konfigurace. Koncepcne by to bylo nejlepsi udelat v LISPu nebo Schemu, diskuze, jestli je to lepsi realizovat jako data, program, DSL by uplne odpadla.
Naucit se novy format je intelektualne minimalne stejne tak narocne jako naucit se schema XML souboru.
Souhlasím, jenže při existenci XML schématu ti hodně pomůže editor – např. takový Emacs ti bude napovídat a kontrolovat syntaxi, aniž bys musel něco řešit – stačí když vedle editovaného souboru bude Relax NG schéma se stejným názvem. V případě nového formátu ti ani neobarví syntaxi, natož aby ti kontroloval správnost a hlásil chyby.
Koncepcne by to bylo nejlepsi udelat v LISPu nebo Schemu
Něříkám, že to nezní zajímavě Můžeš napsat příklad, jak by ta data (ty tři XML soubory) vypadala? Jak by se tam např. zapisovala vazba mezi jazykem a teritoriem a klávesnicí a teritoriem, kde teritorium je definované na jednom místě a má zase vazby na jazyky, klávesnice a další věci?
diskuze, jestli je to lepsi realizovat jako data, program, DSL by uplne odpadla.
A není pak riziko, že taková konfigurace bude příliš mocná a případná chyba nebo záměrná změna v ní může rozhodit celý program a změnit jeho chování? Trochu mi to připomíná situaci, kdy vezmeš data, o kterých někdo tvrdí, že jsou JSON, a zavoláš na nich eval()
. Něco podobného je ten SQL skript, ve kterém by mohly být kromě očekávaných INSERTů i příkazy DELETE, DROP TABLE atd. (i když tohle jde aspoň řešit přes vhodně nastavená práva v DB).
Souhlasím, jenže při existenci XML schématu ti hodně pomůže editorZase DSL muze byt tak jednoduchy, ze si vystacis i s notepadem. Kdyz si budes konfiguraci zapisovat v SQL, editor ti moc nepomuze.
Můžeš napsat příklad, jak by ta data (ty tři XML soubory) vypadala?Nechce se mi nad tim moc premyslet. Ale proc ne treba jako XML prepsane do S-vyrazu?
(languages (language :id aa :iso639-1 aa :iso639-2-t aar :iso639-2-b aar :names ((aa Qafar) (az afarca) ...)) (language ...))
A není pak riziko, že taková konfigurace bude příliš mocná a případná chyba nebo záměrná změna v ní může rozhodit celý program a změnit jeho chování?Tohle je hodne hloupy argument. Zakladem unixove filozofie je, ze uzivatel vi, co dela.
Trochu mi to připomíná situaci, kdy vezmeš data, o kterých někdo tvrdí, že jsou JSON, a zavoláš na nich eval().To je presne to proc tu pisu o LISPu. Protoze tak se na tu konfiguraci muzes divat jako na data, ale soucasne i na DSL, ktery ti muze overovat validitu dat, vytvaret objekty, atd. a soucasne by to odpovidalo predzvykani do konkretniho jazyka, jak navrhujes.
Něco podobného je ten SQL skript, ve kterém by mohly být kromě očekávaných INSERTů i příkazy DELETE, DROP TABLE atd.Ne, neni, viz ma predchozi poznamka.
to si fakt musim pre kazdy jeden ucet RUCNE nastavovat take prkotiny ako je server na prenos suborovNe, u verejne IP to neni potreba.
bud je treba studovat neumerne vela howto / tutorialov / manualov (ak existuju) na to, aby sa rozchodili trivialne featury, alebo je naopak cela vec tak brutalne zautomatizovana, ze do toho rucne nie je mozne vobec zasiahnut.
S čím to srovnáváš? Ono vždycky najdeš něco, co tě bude štvát, ale otázka je, jestli jinde je to lepší. U proprietárního softwaru jsem se nezřídka setkal s tím, že musíš strávit několik dní, jen aby sis rozchodil verzovací systém a vývojové prostředí. Naopak u svobodného softwaru se většinou klade důraz na snižování té vstupní bariéry (autoři chtějí, aby jim ostatní mohli přispět), zdrojáky si stáhneš nějakým obvyklým systémem (mercurial, git, svn, bzr, cvs) a dáš ./configure && make && make install
nebo něco podobného, případně přečteš README a doinstaluješ pár knihoven ze své distribuce.
Svobodný/otevřený software je čím dál úspěšnější a věnuje se mu čím dál víc lidí – s tím přirozeně souvisí i to, že se rozšiřuje rozpětí kvality: od perfektních kousků až po úplný brak.
A trochu tomu nahrávají i weby jako GitHub – pamatuji si jak dřív bylo potřeba žádat o schválení projektu na SourceForge nebo si vytvořit vlastní web a rozjet vlastní verzovací systém. Dneska stačí prsknout zdrojáky na hosting a je to – hodně lidí se pak ani neobtěžuje s tím, aby napsali aspoň abstrakt, k čemu že ten jejich program vlastně slouží.