IRC logs for #buildstream for Thursday, 2019-03-21

gitlab-br-botmarge-bot123 merged MR !1249 (chandan/remove-manifest-conftest->master: MANIFEST.in: Remove conftest.py include) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124900:00
gitlab-br-botcs-shadow approved MR !1247 (bschubert/better-pytest-report->master: tests: when comparing lists/dicts, compare all at once) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124700:01
*** swick has joined #buildstream02:04
*** tristan has joined #buildstream02:57
*** kaiz has joined #buildstream05:06
*** mohan43u has joined #buildstream05:54
*** nimish2711 has quit IRC06:07
*** nimish2711 has joined #buildstream06:11
*** nimish2711 has quit IRC06:26
*** nimish2711 has joined #buildstream06:49
*** mohan43u has quit IRC06:55
*** mohan43u has joined #buildstream06:58
*** mohan43u has quit IRC07:05
*** mohan43u has joined #buildstream07:09
*** mohan43u has quit IRC07:23
*** mohan43u has joined #buildstream07:27
*** mohan43u has quit IRC07:31
*** nimish2711 has quit IRC07:32
*** mohan43u has joined #buildstream07:34
*** mohan43u has quit IRC07:38
*** mohan43u has joined #buildstream07:40
*** nimish2711 has joined #buildstream07:46
*** toscalix has joined #buildstream08:35
*** mohan43u has quit IRC08:39
*** mohan43u has joined #buildstream08:40
*** nimish2711 has quit IRC08:54
gitlab-br-botjuergbi approved MR !1248 (bschubert/lint/logging-format->master: casserver.py: fix logging-format-interpolation error and enable) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124809:00
*** nimish2711 has joined #buildstream09:00
*** mohan43u has quit IRC09:02
*** mohan43u has joined #buildstream09:05
*** mohan43u has quit IRC09:10
*** nimish2711 has quit IRC09:11
benschubertHey tristan , juergbi would you have time to look at https://gitlab.com/BuildStream/buildstream/merge_requests/1070#note_152634508 ? I'll have some time today to finish it, so we could have it go thtough09:12
juergbibenschubert: I already gave my 👍️09:13
benschubertjuergbi: oh, my bad, thanks!09:13
benschubertI'll wait for tristan before finalizing then, thanks a lot!09:13
juergbigitlab doesn't notify about that, so might not have been ieal09:13
*** mohan43u has joined #buildstream09:13
benschubertjuergbi: oh by the way, I'm have a system that takes roughly 10 times more time to push than to pull + build. I'm pushing to 4 caches. Any rough idea where I should look to start diagnosing the slowness?09:24
juergbibenschubert: hm, the CAS server supports batch upload, right?09:25
juergbibst-artifact-server does, but would have to check for buildgrid CAS server09:25
benschubertjuergbi: the cas server is the server bundled in the buildstream codebase09:26
juergbiok, that should be fine, then, assuming it's not ancient09:26
benschubertit's from two weeks ago roughly09:26
benschubertbtw I like the new numbers on https://gitlab.com/BuildStream/buildstream/merge_requests/1070 :)09:27
gitlab-br-botjuergbi approved MR !1247 (bschubert/better-pytest-report->master: tests: when comparing lists/dicts, compare all at once) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124709:28
juergbibenschubert: yes, noticed them as well. so the simpler version is actually faster, at least in the (strict) test case?09:28
benschubertjuergbi: it is!09:28
juergbihooray for simplicity :)09:29
mablanchjuergbi: Any chance you could have a look at this (and merge if it looks good for you) when you'll have time: https://gitlab.com/BuildStream/buildstream/merge_requests/1239?09:30
juergbisure, will take a look09:32
*** raoul has joined #buildstream09:32
benschubertmablanch: does that mean we can easily spawn runners with docker-compose and get a full RE running?09:32
benschubertThe biggest publicly available project using BuildStream is Gnome right? I'd be interested in running some benchmarks more realistic than what we have in bst-benchmarks :)09:33
mablanchbenschubert: Compose greatly help yes. If you want to run some tests manually and scale the number of runners/workers, there is a Compose manifest for BuildStream and usage instructions here: https://gitlab.com/BuildGrid/buildgrid.hub.docker.com09:35
benschubertmablanch: great thanks! Do you know if it's possible to have a remote cache just by changing the url, or is there gotchas?09:36
mablanchbenschubert: You mean a separate artifact cache server, or a totally separate CAS server for the remote execution service?09:38
benschubertmablanch: I mean the action cache and the cas server (using the buildgrid ones, but just on another machine :) )09:39
*** tristan has quit IRC09:43
*** mohan43u has quit IRC09:46
mablanchbenschubert: You'd have to change the worker configuration: they need access to that server. Guess we could provide a separate Compose manifest relying on a remote CAS+AC, that shouldn't be difficult. Feel free to open an issue against that repo.09:47
gitlab-br-bottpollard approved MR !1246 (pointswaves/softreset->master: Added doc's for workspace reset --soft) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124609:47
benschubertmablanch: sure I'll have a play with this and see what I can do or not. Thanks!09:48
*** mohan43u has joined #buildstream09:50
*** jonathanmaw has joined #buildstream09:51
*** mohan43u has quit IRC09:53
*** mohan43u has joined #buildstream09:57
*** tristan has joined #buildstream09:59
gitlab-br-botmarge-bot123 merged MR !1248 (bschubert/lint/logging-format->master: casserver.py: fix logging-format-interpolation error and enable) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124810:03
gitlab-br-botaevri approved MR !1247 (bschubert/better-pytest-report->master: tests: when comparing lists/dicts, compare all at once) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124710:13
gitlab-br-botBenjaminSchubert opened MR !1250 (bschubert/lint/cyclic-import->master: lint: Fix or silence 'cyclic-import' errors and enable pylint for it) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/125010:18
*** nimish2711 has joined #buildstream10:20
*** ChanServ sets mode: +o tristan10:25
tristanbenschubert, I started resolving discussions but then unresolved them, they said the lines changed in a recent diff but as you might be holding on to the discussions as a todo list I unresolved them10:25
benschuberttristan: ok, I'll go through them at the end, thanks! It's indeed a nice todo, given how many things are in there10:26
tristanI like the class method instead of _plugin_lookup() :)10:27
benschuberttristan: great! I wasn't sure you would like it, but it does make things cleaner :)10:28
tristanHmmm10:29
benschubert?10:30
tristanI do dislike exposing the _unique_id directly as an instance variable10:30
benschubertwhy?10:30
tristangenerally having _get_foo() accessors both makes it clear that you are going through a properly policed accessor (as opposed to @property things), and basically also guarantees (as much as possible) that no externals will ever muck about with the Plugin owned variable10:31
tristanWe had incidents too, with the artifact cache size variable being exposed and then external modules poking around and actually modifying it without even a mutator10:32
tristanIf we can get to a place where nothing is exposed directly on instances, would be nice even10:32
benschubertFor the accessor, that's true for other languages, the style guides in python are pretty much against that, especially since it has a runtime cost10:33
benschubertThe second part is more worrying, but we have tests and code reviews10:33
tristanOur style guide pretty intentionally goes against pythons in this regard10:33
tristanCant python optimize this away ?10:33
tristanwhen doing it's .pyc files ?10:34
benschubertNo, I could well be modifying this function and do something else instead at runtime. So you can't change that10:34
tristanThats something that we should be able to disallow as well10:34
tristanWe only allow overriding methods which are specifically advertised to be abstract/overridable10:35
benschuberttristan: we can disallow this in the styleguide, the interpreter doesn't has this knowledge10:35
benschubertAnd if the choice is between going through the get() method or having Element have their own unique_id, I'd rather go for the memory hit and have two different ids :)10:35
tristanWe need that guarantee that people wont go haphazardly overriding methods that the parent class doesnt expect10:35
tristanI did go looking for a way to disallow it, it can be possible but would require some custom decorator10:36
benschuberttristan: agreed, as we need the guarantee that people go through a modifier function to modify anything on an another class :)10:36
juergbiI guess pylint can't help us here?10:36
benschubertjuergbi: it could, would require writing our own plugin10:36
tristan(i.e. a way for disallowing methods being overridden where they are not advertized to be abstract/overridable)10:37
benschubertjuergbi: we could have two linter configuration, one for not changing another class attribute, one for not overriding a method10:37
tristanRight linting in both cases would be preferable10:37
benschuberttristan: if you are happy with having custom linter plugins for this, I'm happy to try something (not doing it for nothing since it's kinda annoying to work with)10:38
*** lachlan has joined #buildstream10:39
tristanI would, I would love to go through all the classes and decorate only the ones which are intended to be overridable10:39
tristan(only the methods)10:40
* tristan not typing very well today10:40
tristanenglish broken10:40
benschuberttristan: let's discuss this afterwards then, I think that would be nice indeed. And let's focus on 1070 for now ok? That way we could be able to finish it today10:40
tristanbenschubert, Alright, lets keep it the way it is with exposed _plugin_id variable then10:42
benschubertok great10:42
benschubertI'll move the PQ in types, update its doc to make it private, and add the doc to the new _lookup method. Then cleanup the history10:43
*** mohan43u has quit IRC10:43
tristanWhen doing the other linting things, we can rethink our style guide around these parts https://docs.buildstream.build/CONTRIBUTING.html#instance-variables, and then we'll want to make things consistent as much as possible10:43
benschuberttristan: btw this new version is already a tad faster on 88 builders10:43
benschubert*8 not 8810:43
tristanhttps://docs.buildstream.build/CONTRIBUTING.html#abstract-methods also mentions about abstract method policy, we can update that pointing to a new decorator10:44
benschuberttristan: so access through variable, mutation through method, correct? (with a linter that makes sure that it works)10:44
tristanI dont think we can really have that policy10:45
tristanacross the board10:45
tristanit makes sense for some things, but in other cases it makes sense to trigger code in response to accessing something10:45
tristanlazy resolution of things, etc10:45
benschubertabsolutely, but that is where properties are great :)10:45
tristanWhat I dont like about properties is that we dont know on the caller side that a method is being invoked or not10:46
tristanbenschubert, anyway I think this is a conversation for another day10:46
benschubertsure!10:47
benschubertI'll start with the abstract method first :)10:47
tristanlets start by wrapping up !1070 :)10:47
gitlab-br-botMR !1070: WIP: Refactor _update_state() to be called only when needed https://gitlab.com/BuildStream/buildstream/merge_requests/107010:47
* tristan will backport that and csoriano's checkouts should work again10:48
tristanvalentind, is there anything else that needs fixing in 1.2.5, asides from #919 ?10:50
gitlab-br-botIssue #919: 'bst build <elem>' does not assemble all required elements in some circumstances https://gitlab.com/BuildStream/buildstream/issues/91910:50
tristanadds68, jjardon same ^^^ ?10:50
valentindtristan, not to my knowledge10:50
* tristan doesnt think so either10:50
valentindSomething unrelated, tristan, is there a way to tell buildstream what shell interpreter to use to run build commands? I do not think there is. But I might have missed something.10:51
valentindBecause not being able to use extglob in bash for example is annoying.10:52
valentindOr subshell redirections.10:52
tristanvalentind, Not really unfortunately10:53
tristanvalentind, That is decided by BuildElement at least; not in the base classes10:53
valentindI thought it was hardcoded in the sanbox.10:54
tristancommands listed in build elements are hard coded to be executed with 'sh', -c command10:54
tristangah10:54
tristan'sh', '-c', 'command' + '\n'10:55
tristanvalentind, no at least not that10:55
valentindI see it.10:55
tristanvalentind, We can run whatever program with, e.g. `bst shell`, I think that is well regression tested too :)10:55
valentindscriptelement also has it.10:55
tristanyep, that's what they're doing10:55
tristanvalentind, You could workaround with a custom `sh` and adjusting PATH I suppose10:56
tristanbut seems like good material to file an issue about10:56
valentindWhen bash is called as sh it disables a lot of things.10:56
valentindSo we cannot use symlinks for that.10:57
tristanmaybe something openended without a specific solution, just 'currently with build elements there is no way to control what shell to use'10:57
benschubertFor that I would even go further and say "we need a shell to build, we should be able to specify what to call", because some programs don't need to be in a shell :)10:58
valentindYes.10:58
tristanvalentind, I think it's the sane default, though - ideally we want to coerce people into writing valid posix shell that'll still work with most interpretors10:58
abderrahim[m]Jjj10:58
valentindAnd who knows. Maybe masochists want to use powershell. They should be able to.10:58
tristanYeah, rpm for instance allows specifying the interpretor for it's pre/post things10:59
tristanAnd if we have similar support in BuildStream, it makes the job of automating conversions of rpm based build stack into .bst files easier10:59
tristan*stacks11:00
tristanseems like also a masochistic thing to do, but we've seen it before ;-)11:00
benschubertvalentind: or python, python is a very nice shell to use :)11:00
*** lachlan has quit IRC11:02
valentindnode.js XD11:02
valentindtristan, should it be a variable which value we split with shlex.split?11:06
valentindIt is also in element.py for integration commands.11:09
tristanerr shlex, I recall not being happy about using shlex11:09
tristanvalentind, ah yes, but specifically for integration commands at least, it's intentionally not *too* deeply entrenched11:09
valentindWe need somehow to transform a string to an argv11:10
*** lachlan has joined #buildstream11:10
tristanvalentind, I don't like that idea11:10
tristanI would rather not presume to know how it's done for a given string and quoting style, or to trust a library to know how11:10
tristanbetter to never know11:10
valentindWell, if we put shlex.quote(cmd) in the command, we know for sure that shlex.split will know how to get back the right string.11:14
*** alatiera has joined #buildstream11:14
valentindWe could also have our own quote and unquote algorithm.11:16
valentindAnd document it.11:17
tristanUgh, that snuck back in grep reveals :-S11:19
tristanvalentind, Better to just use explicit yaml11:19
tristanand not mess with the strings the user feeds to the shells11:19
*** lachlan has quit IRC11:20
*** lachlan has joined #buildstream11:21
gitlab-br-botmarge-bot123 merged MR !1246 (pointswaves/softreset->master: Added doc's for workspace reset --soft) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124611:24
benschubertjuergbi, tristan !1070 has been pushed. I think we are good to go, mind having a last look?11:24
gitlab-br-botMR !1070: WIP: Refactor _update_state() to be called only when needed https://gitlab.com/BuildStream/buildstream/merge_requests/107011:24
gitlab-br-botBenjaminSchubert opened (was WIP) MR !1070 (bschubert/pipeline->master: Refactor _update_state() to be called only when needed) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/107011:24
*** lachlan has quit IRC11:28
gitlab-br-botjuergbi approved MR !1070 (bschubert/pipeline->master: Refactor _update_state() to be called only when needed) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/107011:28
benschuberttristan: about https://gitlab.com/BuildStream/buildstream/merge_requests/1070/diffs#note_152850346, is this really an assert statement we want? Or a real concrete exception (asserts can be disabled at runtime, exception not)11:36
*** mohan43u has joined #buildstream11:44
*** lachlan has joined #buildstream11:47
*** lachlan has quit IRC11:54
*** lachlan has joined #buildstream12:06
*** lachlan has quit IRC12:14
gitlab-br-botmartinblanchard opened issue #968 (Use GitLab CI services instead of Compose for rexec testing) on buildstream https://gitlab.com/BuildStream/buildstream/issues/96812:33
*** raoul has quit IRC12:34
*** lachlan has joined #buildstream12:42
*** nimish2711 has quit IRC12:51
tristanbenschubert, Hmmm, this is an interesting distinction12:52
tristanone I was not really aware of12:52
tristanbenschubert, Right now we only have 2 categories of error; (A) Fatal errors which get reported to the user (B) Programming errors which are caught with exceptions12:53
tristan(A) is all of the exceptions which derive from BstError12:54
tristanbenschubert, that particular assert definitely is a programming error / bug if it hits, so we definitely want a stack trace if it ever hits12:54
tristanoh sorry, correction to the above:  (B) Programming errors which are caught with assertions12:55
tristanSo we usually use assert statements for anything that is not a fatal error that should be reported intelligibly to the user12:55
tristanNow the question is, should we instead use exceptions in some cases, splitting (B) into two separate categories12:56
tristanbenschubert, For right now, we definitely want an assert; if we want to change that policy I think we should think about that and possibly change the codebase if we do decide on changing that12:57
tristanI suppose that if you disable asserts at runtime, that means you are very confident that your code has no bugs12:58
tristanin which case I think the current policy is okay right ?12:58
tristanbenschubert, for the specific line, I would have raised AssertionError() because it's just nicer than doing `assert False, "message"`12:59
*** nimish2711 has joined #buildstream13:01
benschuberttristan: in order to keep the exact same behavior, it would be: if __debug__: raise AssertionError(message). Would that be ok?13:04
tristanEh, I think I prefer assert False :)13:05
tristanheh13:05
benschubertand the reason to remove the assertion is that in "python world" they are often used for debugging purposes, and thus removing them at runtime should still work (Like when you compile with gcc -O{2,3})13:05
benschubertSo not necessarily that you are confident ;)13:06
benschubertok I will change this, thanks13:06
tristanbenschubert, I think that right now we dont use it like this anyway - but it might an interesting distinction to make if for example; we were to assert valid arguments in all function entry points13:07
benschubertagreed13:08
tristanthen we might introduce a raw Bug() exception to use instead of the currently existing assertions13:08
*** alatiera_ has joined #buildstream13:08
*** alatiera has quit IRC13:08
*** alatiera_ is now known as alatiera13:09
gitlab-br-botjuergbi approved MR !1238 (raoul/source-key-fix->master: Source key fix) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/123813:21
csorianohey, for the soc projects at https://wiki.gnome.org/Outreach/SummerOfCode/2019/Ideas have you decided who are going to be the mentors? We would need that relatively soon13:31
csorianoalso, for the idea of Buildstream integration with Builder, have you checked with Christian Hergert?13:31
tristancsoriano, We had a few talks yes, but it's still a bit hazy to me13:35
*** raoul has joined #buildstream13:35
tristanWe had a long discussion at GUADEC which mostly covered implementation details but I think I'm missing a big picture here13:35
tristanalso, note that adds68 is working on a BuildStream plugin for Builder right now13:35
csorianothat is for Builder?13:35
tristanRight, we had discussion at GUADEC in a Builder hackfest about how to integrate BuildStream as a plugin13:36
csorianook, we would just need an ack from Christian that the gsoc project for that is okay13:36
csorianocould you (or the potential mentor) contact him and let me know?13:36
benschuberttristan: also is !1250 in line with what you had in mind about cyclic imports?13:52
gitlab-br-botMR !1250: lint: Fix or silence 'cyclic-import' errors and enable pylint for it https://gitlab.com/BuildStream/buildstream/merge_requests/125013:52
tristancsoriano, Ok I'll put it on my list... I don't know if we have a mentor13:53
tristancsoriano, i.e. any discussions we ever had were about the actual work, not about a gsoc project13:54
csorianotristan: ah I see... well now there are two gsoc projects (which is nice), iirc it was added by jjardon after I proposed to him the idea of doing gsoc from the Buildstream team13:55
tristanAh, there are certainly conversations which happen that I'm not privy to13:56
tristanWhich is nice :)13:56
tristanjjardon, any idea about the above ? have an idea for a mentor for a gsoc project ?13:57
* tristan wonders just how time consuming mentoring is, and wouldnt want to volunteer unless he was really certain that he could provide enough attention13:58
csorianotristan: 5h per week min14:16
csorianoit's not a small commitment, but also it becomes part of the work once they start doing MR etc. After all, reviewing MRs and such is something we do every day...14:16
tristanRight, but I would certainly want to be able to spend enough 1:1 time14:23
*** lachlan has quit IRC14:52
gitlab-br-botmarge-bot123 closed issue #703 (Queues do too much work during scheduling) on buildstream https://gitlab.com/BuildStream/buildstream/issues/70314:55
gitlab-br-botmarge-bot123 merged MR !1070 (bschubert/pipeline->master: Refactor _update_state() to be called only when needed) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/107014:55
*** lachlan has joined #buildstream14:56
*** tristan has quit IRC14:57
*** lachlan has quit IRC15:03
*** lachlan has joined #buildstream15:06
*** lachlan has quit IRC15:22
*** lachlan has joined #buildstream15:30
*** tristan has joined #buildstream15:39
jjardontristan: those ideas were put there in a hurry (you were pinged in IRC before I put them); we need to decide if we go ahead with them, change them or not do them at all because lack of mentor time15:48
*** ChanServ sets mode: +o tristan15:49
tristanjjardon, yup, was quite a while back I think... still it would be nice if someone wants to mentor and has the time :)15:49
*** lachlan has quit IRC15:55
gitlab-br-botmarge-bot123 merged MR !1245 (aevri/dirname_basename->master: tests: str(datafiles) instead of a longer thing) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124515:56
*** alatiera has quit IRC16:05
benschuberttristan: !1177 can be closed now correct?16:11
gitlab-br-botMR !1177: WIP: tests/frontend/workspace.py: Test that all elements build with workspace in play https://gitlab.com/BuildStream/buildstream/merge_requests/117716:11
*** lachlan has joined #buildstream16:14
*** lachlan has quit IRC16:20
*** lachlan has joined #buildstream16:34
gitlab-br-botmarge-bot123 merged MR !1227 (aevri/you_only_write_once->master: cleanupjob, cascache: don't write cache size twice) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/122716:37
gitlab-br-botaevri closed issue #957 (Race on temporary file in .cache/buildstream/cas/) on buildstream https://gitlab.com/BuildStream/buildstream/issues/95716:39
benschubert\Is gnome the biggest public project using BuildStream? Is there anything bigger (I'm speaking junctions included)16:44
adds68benschubert, if what you mean by junctions included, is that they include freedesktop-sdk, then yes it is16:47
benschubertadds68: Great, thanks! I'll be able to do some benchmarking then :)16:48
gitlab-br-botBenjaminSchubert opened MR !1253 (bschubert/fix-double-lookup->master: plugin.py: Don't make a double lookup in the plugin table to get one) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/125316:50
benschuberttristan: ^ what you had noticed on the previous MR16:50
*** mohan43u has quit IRC17:14
gitlab-br-botjonathanmaw opened (was WIP) MR !1251 (jonathan/skip_schedule_attempt->master: Skip scheduling assembly when calculating cache key for staging junctions) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/125117:23
gitlab-br-botmarge-bot123 merged MR !1247 (bschubert/better-pytest-report->master: tests: when comparing lists/dicts, compare all at once) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/124717:40
gitlab-br-botaevri opened issue #970 (bst artifact pull: 'artifact not found' is success) on buildstream https://gitlab.com/BuildStream/buildstream/issues/97017:50
*** jonathanmaw has quit IRC17:54
*** lachlan has quit IRC17:58
*** toscalix has quit IRC17:58
*** lachlan has joined #buildstream18:02
*** tpollard has quit IRC18:05
gitlab-br-botaevri approved MR !1253 (bschubert/fix-double-lookup->master: plugin.py: Don't make a double lookup in the plugin table to get one) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/125318:31
*** alatiera has joined #buildstream18:32
*** lachlan has quit IRC18:37
*** raoul has quit IRC18:40
*** nimish2711 has quit IRC19:07
*** nimish2711 has joined #buildstream19:07
gitlab-br-botmarge-bot123 merged MR !1238 (raoul/source-key-fix->master: Source key fix) on buildstream https://gitlab.com/BuildStream/buildstream/merge_requests/123819:11
*** rdale has quit IRC19:40
*** nimish2711 has quit IRC19:59
*** alatiera has quit IRC22:52

Generated by irclog2html.py 2.15.3 by Marius Gedminas - find it at mg.pov.lt!