Learning jQuery Deferreds published

January 7th, 2014

learning-jQuery-deferredsNicholas Tollervey and I have written a book, Learning jQuery Deferreds, published by O’Reilly.

If you’ve been a reader of this blog over the years, you may have noticed that I’m very fond of deferreds (also known as promises or futures). I’ve mainly been using them in Python with Twisted, and a couple of years ago was happy to notice that jQuery also has a (somewhat different) version of deferreds. Asking around, it soon became clear that although there are tens of millions of programmers who’ve used jQuery, very few of them have ever used deferreds. E.g., at the jQuery conference in San Francisco in 2012, only about 25% of the audience in a talk on deferreds. There are about 19,000 results for “deferred” on StackOverflow.

This seemed like a perfect storm: a fantastically cool and empowering technology that I love thinking and writing about, built in to a ubiquitous web library, in use by millions of programmers, and yet somehow not widely known or used.

The book tries to really teach deferreds. There are 18 real-life examples, along with 75 challenges (and solutions). We’ve tried to get readers into deferreds the way we are, to be provocative, to try and get you thinking and scratching your head. To get you to see how cool and elegant working with deferreds can be. In short, to make you one of us.

Although the book focusses on jQuery’s flavor of deferreds, we wrote it with a much broader aim: to be just as useful to people working with any flavor of deferreds in any language. The concepts behind deferreds are few and simple. Even if you’re not a jQuery user or a JavaScript programmer, we hope you’ll enjoy and benefit from the book.

If you’re curious, the animal on the cover is a musky rat-kangaroo. O’Reilly chose it for us. When I first saw it, I mailed them and Nicholas to say it looked “overfed, passive, and thoughtful” to which Nicholas replied that it resembled me. The O’Reilly toolchain is modern and fun, employing AsciiDoc and a shared Git repo. We wrote 30,817 words and 2,301 lines of Javascript. There’s a source code repo for all the book examples, at https://github.com/jquery-deferreds/code. We spent six months on the book, during which I usually spent 1-2 days a week on it. It was a blast to write.

If you’d like to buy a copy, use AUTHD as a discount code on the O’Reilly site and you’ll receive 40% off the print or 50% off the e-book. Please let me know what you think, and/or leave a review on the O’Reilly (or Amazon) site. Have fun, and thanks!

txdlo – a Twisted deferred list observer

December 30th, 2013

Last night I wrote txdlo, a Python package that provides a class called DeferredListObserver. As you might guess, it lets you observe callback and errback events from a list of Twisted deferreds. You can add observers that will be passed information about deferreds firing. You can add deferreds to the observed list at any time, which is very useful if you’re dynamically creating deferreds that you want to monitor.

The class can be used to easily build functions or classes that provide deferreds that fire when arbitrary combinations of events from the observed deferreds have occurred.

For example you can write functions or classes that support deferreds that:

  • Implement Twisted’s DeferredList or simple variants of it, or that let you separate the various behaviors of DeferredList into simpler functions.
  • Provide a deferred that fires when N of the observed deferreds have fired.
  • Provide a deferred that ignores errors until one of the observed deferred succeeds, only firing with an error if all the observed deferreds fail.
  • Or (a more involved example), suppose you have 3 methods that can return you a user’s avatar: a fast local cache, a filesystem, and a slow network call to Gravatar. You want to write a deferred-returning function that launches all three lookups at once and fires its deferred with the first answer. But if the cache and/or filesystems fails first, you don’t want to trigger an error, you instead want to take the result from Gravatar and add it to the cache and/or filesystem, as well firing the returned deferred with the result (wherever it comes from). Only if all three lookups fail do you want to errback the deferred you returned.

Here’s a sample example usage, which shows how to use DeferredListObserver to build a simplified version of Twisted’s DeferredList class as a function:

from twisted.internet.defer import Deferred, succeed
from txdlo import DeferredListObserver

def deferredList(deferreds):
    """
    Return a deferred that fires with a list of (success, result) tuples,
    'success' being a boolean.

    @param deferreds: a C{list} of deferreds.
    @return: a L{twisted.internet.defer.Deferred} that fires as above.
    """

    if len(deferreds) == 0:
        return succeed([])

    dlo = DeferredListObserver(maintainHistory=True)
    map(dlo.append, deferreds)
    deferred = Deferred()

    def observer(*ignore):
        if dlo.pendingCount == 0:
            # Everything in the list has fired.
            resultList = [None] * len(deferreds)
            for index, success, value in dlo.history:
                resultList[index] = (success, value)
            deferred.callback(resultList)

    dlo.observe(observer)

    return deferred

You can grab the code, read more about usage, and see several other examples at https://github.com/terrycojones/txdlo.

Promises are first-class objects for function calls

September 12th, 2013

Have you ever programmed in a language without functions as first-class objects? You can’t return a function from a function, can’t pass a function as an argument, and you certainly can’t make anonymous functions on the fly. Remember how liberating, empowering, and flexible it felt when you moved to a language with functions as first-class objects?

Yesterday, after 7 years of working with deferreds/promises/futures (call them what you will), thinking carefully about them thousands of times, mailing and blogging about them, giving talks about them, and now writing a book about them, I realized there is a very simple way to look at them:

Promises are first-class objects for function calls.

In other words, a promise gives you a way to make a function call and to pass around that function call. Return it from a function, pass it as an argument, put it in a data structure, etc. Given a promise, you can arrange to process the result of the function call or its error.

To be a bit more abstract: A promise is a time-independent reification of a function call.

By “time-independent” I mean that if you get a promise from somewhere, you don’t have to worry whether the underlying function call has already completed, is currently in progress, or has yet to run. Depending on the promise implementation there may be no way to find out. The point is that you don’t have to care and you shouldn’t care.

That’s all I’ll say for now, except for a final comment on naming.

I think “promise” is a better name than “future” or “deferred” because the latter two feel more like they’re referring to an event yet to happen. A promise feels more neutral: you could easily be making a promise about something you’ve already done. Many explanations of promises/deferreds/futures stress that they are something that will hold the result of a computation that’s not yet completed. That’s certainly a common usage, but it’s only part of the picture. Describing promises as a reification of a function call takes the time factor completely out of the picture.

Here’s a small Javascript function to memoize a (single-argument) function to illustrate the point:

var memoize = function(fn) {
    var promises = {};

    return function(arg) {
        var promise = promises[arg];
        if (!promise) {
            promise = promises[arg] = $.when(fn(arg)).promise();
        }
        return promise;
    };
};

The memoization cache is full of promises, not underlying function results. Some of the function calls may have completed, some may be underway. I’m using the jQuery $.when function as a convenience (that’s an irrelevant detail, don’t let it distract you). The promises stay in the cache indefinitely (no eviction, for simplicity), holding promises for function calls from the past.

Time is not an issue here.

Specifically, and in contrast, think about what happens with non-promise memoization. What happens if a first call comes in with an argument X, but before fn(X) has finished computing there is another call with argument X? Then another and another and another? You wind up calling fn(X) many times. I.e., doing exactly the thing you were trying to avoid! And there’s nothing you can do about it.

If you’re interested to review the book I’m writing with Nicholas Tollervey on jQuery deferreds, please email me or leave a comment below. Most of the book is not really specific to jQuery’s flavor of deferreds.

The mockery pervading human affairs in all their aspects

September 5th, 2013

tacitusFrom Tacitus, on the eventual rise of Claudius to Roman emperor, from The Annals of Imperial Rome (Penguin Classics, p127):

The more I think about history, ancient or modern, the more ironical all human affairs seem. In public opinion, expectation, and esteem no one appeared a less likely candidate for the throne than the man for whom destiny was secretly reserving it.

The Cambridge Companion to Tacitus (p169) translates as follows:

The more I consider recent events or those in the distant past, the more I am confronted by the mockery pervading human affairs in all their aspects. For in public opinion, expectations and esteem, no one was less likely a candidate for the throne than the man whom fortune was secretly holding in reserve.

1793 viruses!

July 30th, 2013

In case you missed it, I spent ten days in hospital this past May (2013).

When they took the skin biopsy from my arm, I got them to take 2 samples. One of them, along with a throat and skin swab was later sent to the virologists I do some work with in the Viroscience Lab at the Erasmus Medical Center (EMC).

I got the sequence data back about a week ago and have been looking at them, firstly via BLAST and then using a bunch of code I’ve been writing lately. There are 115K reads from 6 preparations (RNA and DNA protocols for each of the 3 samples). These come from a “next generation” sequencer, a Roche 454. The next generation sequencing involves using random primers to indiscriminately match genetic material. My BLAST output files are about 82Mb in total (this is relatively small, some of my other data sets are about 30Gb). I BLASTed against a viral subset alias nucleotide database that I made from the full NCBI Nucleotide Database, excluding all bacteriophage viruses. There are about 1.3M viral sequences in the subset db.

I wont go into details, but wanted to dump a bit of data that’s pretty amusing / interesting. Just to give the EMC folks an idea of the scale of diversity I am seeing, I grepped out all “complete genome” hits from all the BLAST output. I chucked out suffixes in the sequence titles that matched the regex (nearly )?complete genome|isolate|strain|subtype).* and then stripped the titles of any text beyond the string “virus” in the title (this step collapses a lot of virus strain information that should really be kept). Then, do a unique sort and…. it turns out I have reads matching at least 1793 viruses.

I feel like the subject of a metagenomics study. At the hospital, once the chickenpox tests had come back negative, they threw a ton of tests at my samples and everything was negative. Otherwise, I’d really be worried :-)

Given the list of sequence matches, it feels like the only plausible explanation is that I’m actually dead and that this is all just a simulation.

Here’s the sorted list of virus names (read counts omitted). I find it pretty amazing. I don’t know what it all means, but I’m planning to find out more by writing more code and learning more.

Abalone herpesvirus
Abalone shriveling syndrome-associated virus
Abelson murine leukemia virus
Abutilon mosaic virus
Acanthamoeba castellanii mamavirus
Acanthamoeba polyphaga mimivirus
Acanthamoeba polyphaga moumouvirus
Acanthocystis turfacea Chlorella virus
Acheta domestica densovirus
Achimota virus
Acidianus bottle-shaped virus
Acidianus filamentous virus
Acidianus filamentus virus
Acidianus spindle-shaped virus
Aconitum latent virus
Acute bee paralysis virus
Adelaide River virus
Adeno-associated virus
Adenovirus
Adoxophyes honmai enomopoxvirus
Adoxophyes honmai nucleopolyhedrovirus
Adoxophyes orana granulovirus
Adoxophyes orana nucleopolyhedrovirus
Aedes aegypti densovirus
Aedes flavivirus
Aedes taeniorhynchus iridescent virus
Aeropyrum pernix K1 DNA
Aeropyrum spring-shaped virus
African cassava mosaic Burkina Faso virus
African cassava mosaic virus
African green monkey polyomavirus
African oil palm ringspot virus
African swine fever virus
Ageratum enation alphasatellite
Ageratum enation virus
Ageratum leaf curl virus
Ageratum yellow vein China virus
Ageratum yellow vein Singapore alphasatellite
Ageratum yellow vein virus
Agropyron mosaic virus
Agrotis ipsilon multiple nucleopolyhedrovirus
Agrotis segetum granulovirus
Agrotis segetum nucleopolyhedrovirus
Aichi virus
Aleutian mink disease parvovirus
Alfuy virus
Algerian watermelon mosaic virus
Alkhumra hemorrhagic fever virus
Allium virus
Alpaca respiratory coronavirus
Alphavirus
Alstroemeria virus
Alternanthera mosaic virus
Alternanthera yellow vein virus
Ambystoma tigrinum stebbensi virus
American hop latent virus
Amphotropic murine leukemia virus
Amsacta moorei entomopoxvirus
Anatid herpesvirus
Andean potato latent virus
Andean potato mild mottle virus
Anguillid herpesvirus
Anopheles gambiae densonucleosis virus
Antheraea pernyi nucleopolyhedrovirus
Anticarsia gemmatalis nucleopolyhedrovirus
Aotine herpesvirus
Aphid lethal paralysis virus
Apium virus
Apocheima cinerarium nucleopolyhedrovirus
Apodemus sylvaticus papillomavirus
Apple chlorotic leaf spot virus
Apple green crinkle associated virus
Apple stem grooving virus
Apple stem pitting virus
Apricot latent virus
Apricot pseudo-chlorotic leaf spot virus
Aravan virus
Archaeal BJ1 virus
Arctic ground squirrel hepatitis B virus
Armigeres subalbatus virus
Arracacha mottle virus
Artemisia virus
Artibeus jamaicensis parvovirus
Asclepias asymptomatic virus
Asian prunus virus
Asparagus virus
Astrovirus
Ateles paniscus polyomavirus
Ateline herpesvirus
Atlantic salmon paramyxovirus
Atlantic salmon swim bladder sarcoma virus
Aurantiochytrium single-stranded RNA virus
Australian bat lyssavirus
Autographa californica nucleopolyhedrovirus
Avastrovirus
Avian adeno-associated virus
Avian adenovirus
Avian bornavirus
Avian carcinoma virus
Avian encephalomyelitis virus
Avian endogenous retrovirus
Avian gyrovirus
Avian hepatitis E virus
Avian infectious bronchitis virus
Avian leukemia virus
Avian leukosis virus
Avian metapneumovirus
Avian myelocytomatosis virus
Avian nephritis virus
Avian paramyxovirus
Avian pneumovirus
B19 virus
BK polyomavirus
Babanki virus
Baboon endogenous virus
Baboon enterovirus
Bacillus virus
Bagaza virus
Bamboo mosaic virus
Baminivirus
Banana bract mosaic virus
Banana mild mosaic virus
Banana streak CA virus
Banana streak GF virus
Banana streak IM virus
Banana streak Imove virus
Banana streak Mys virus
Banana streak Mysore virus
Banana streak UA virus
Banana streak UI virus
Banana streak UL virus
Banana streak UM virus
Banana streak virus
Bandicoot papillomatosis carcinomatosis virus
Barbel circovirus
Barley dwarf virus
Barley yellow dwarf virus
Barmah Forest virus
Basella rugose mosaic virus
Bat Paramyxovirus
Bat SARS CoV Rs672/2006
Bat SARS coronavirus
Bat adenovirus
Bat betaherpesvirus
Bat circovirus
Bat coronavirus
Bat hepatitis virus
Bat hepevirus
Bat picornavirus
Bat polyomavirus
Bat sapovirus
Bathycoccus sp. RCC1105 virus
Beak and feather disease virus
Bean common mosaic necrosis virus
Bean common mosaic virus
Bean leafroll virus
Bean yellow mosaic virus
Beauveria bassiana RNA virus
Bebaru virus
Beet black scorch virus
Beet chlorosis virus
Beet curly top Iran virus
Beet curly top virus
Beet mild curly top virus
Beet mild yellowing virus
Beet mosaic virus
Beet severe curly top virus
Beet soil-borne mosaic virus
Beet western yellows virus
Beet yellows virus
Beilong virus
Bell pepper endornavirus
Bell pepper mottle virus
Berrimah virus
Betacoronavirus
Bettongia penicillata papillomavirus
Bhendi yellow vein Bhubhaneswar virus
Bhendi yellow vein mosaic betasatellite
Bhendi yellow vein mosaic virus
Bidens mottle virus
Black raspberry virus
Blackberry virus
Blackeye cowpea mosaic virus
Blattella germanica densovirus
Blue squill virus
Blueberry latent virus
Blueberry red ringspot virus
Blueberry scorch virus
Blueberry virus
Bluegill picornavirus
Bocavirus
Bokeloh bat lyssavirus
Bombyx mandarina nucleopolyhedrovirus
Bombyx mori Macula-like virus
Bombyx mori NPV
Bombyx mori densovirus
Bombyx mori nuclear polyhedrosis virus
Border disease virus
Borna disease virus
Bos grunniens papillomavirus
Botryotinia fuckeliana totivirus
Botrytis virus
Bougainvillea spectabilis chlorotic vein-banding virus
Bovine adeno-associated virus
Bovine adenovirus
Bovine astrovirus
Bovine coronavirus
Bovine enterovirus
Bovine ephemeral fever virus
Bovine foamy virus
Bovine herpesvirus
Bovine hungarovirus
Bovine kobuvirus
Bovine leukemia virus
Bovine papillomavirus
Bovine papular stomatitis virus
Bovine parainfluenza virus
Bovine parvovirus
Bovine polyomavirus
Bovine respiratory coronavirus
Bovine respiratory syncytial virus
Bovine rhinovirus
Bovine syncytial virus
Bovine viral diarrhea virus
Brassica yellows virus
Breda virus
Brevicoryne brassicae picorna-like virus
Bromus catharticus striate mosaic virus
Brugmansia mosaic virus
Brugmansia suaveolens mottle virus
Budgerigar fledgling disease polyomavirus
Buggy Creek virus
Bulbul coronavirus
Bundibugyo ebolavirus
Bussuquara virus
Butterbur mosaic virus
Cacao swollen shoot virus
Cactus mild mottle virus
Cactus virus
Cafeteria roenbergensis virus
Caladenia virus
Calf-giraffe coronavirus
Calibrachoa mottle virus
Calicivirus
California sea lion anellovirus
California sea lion polyomavirus
Callitrichine herpesvirus
Camelpox virus
Camelus dromedarius papillomavirus
Canary circovirus
Canary polyomavirus
Canarypox virus
Canine adenovirus
Canine bocavirus
Canine circovirus
Canine coronavirus
Canine distemper virus
Canine kobuvirus
Canine minute virus
Canine oral papillomavirus
Canine papillomavirus
Canine parvovirus
Canine picodicistrovirus
Canine picornavirus
Canine respiratory coronavirus
Canine vesivirus
Canna Yellow Streak Virus from United Kingdom
Capra hircus papillomavirus
Caprine arthritis encephalitis virus
Caprine arthritis-encephalitis virus
Cardioderma polyomavirus
Cardiospermum yellow leaf curl virus
Caretta caretta papillomavirus
Carnation etched ring virus
Carnation mottle virus
Carrot mottle mimic umbravirus
Carrot mottle virus
Carrot necrotic dieback virus
Carrot red leaf virus
Carrot yellow leaf virus
Cassava brown streak virus
Cassava common mosaic virus
Cassava latent virus
Cassava vein mosaic virus
Catharanthus yellow mosaic virus
Cauliflower mosaic virus
Cavally virus
Caviid herpesvirus
Cebus albifrons polyomavirus
Cedar virus
Celery mosaic virus
Cercopithecine herpesvirus
Cercopithecus erythrotis polyomavirus
Cercopithicine herpesvirus
Cereal yellow dwarf virus
Cervus elaphus papillomavirus
Cestrum yellow leaf curling virus
Chaerephon polyomavirus
Chaetoceros lorenzianus DNA Virus DNA
Chaetoceros salsugineum DNA virus
Chaetoceros tenuissimus DNA virus
Chandipura virus
Chaoyang virus
Chayote mosaic tymovirus
Chelonia mydas papillomavirus
Chenopodium leaf curl virus
Chenopodium mosaic virus
Cherry green ring mottle virus
Cherry mottle leaf virus
Cherry necrotic rusty mottle virus
Cherry rusty mottle associated virus
Cherry virus
Chiba virus
Chicken anemia virus
Chicken astrovirus
Chicken parvovirus
Chickpea chlorosis Australia virus
Chickpea chlorosis virus
Chickpea chlorotic dwarf virus
Chickpea chlorotic stunt virus
Chickpea redleaf virus
Chickpea yellows mastrevirus
Chikungunya virus
Chilli leaf curl India virus
Chilli leaf curl virus
Chilli ringspot virus
Chilli veinal mottle virus
Chilo iridescent virus
Chiltepin yellow mosaic virus
Chimeric Tick-borne encephalitis virus
Chimpanzee adenovirus
Chimpanzee alpha-1 herpesvirus
Chimpanzee polyomavirus
Chimpanzee stool associated circular ssDNA virus
Chimpanzee stool avian-like circovirus
Chinese yam necrotic mosaic virus
Chlamys acute necrobiotic virus
Chloris striate mosaic virus
Choristoneura biennis entomopoxvirus
Choristoneura fumiferana MNPV polyhedrin
Choristoneura fumiferana defective nucleopolyhedrovirus
Choristoneura occidentalis granulovirus
Choristoneura rosaceana entomopoxvirus
Chrysanthemum virus
Chrysodeixis chalcites nucleopolyhedrovirus
Circoviridae bovine stool/BK/KOR/2011
Circovirus
Circulifer tenellus virus
Citrus chlorotic dwarf associated virus
Citrus leaf blotch virus
Citrus sudden death-associated virus
Citrus tatter leaf virus
Citrus tristeza virus
Citrus yellow mosaic virus
Citrus yellow vein clearing virus
Clanis bilineata nucleopolyhedrosis virus
Classical swine fever virus
Clerodendron yellow mosaic virus
Clitocybe odora virus
Clitoria yellow mottle virus
Cloning vector pEAV030 containing cDNA of Equine arteritis virus
Clostera anachoreta granulovirus
Coastal Plains virus
Cocal virus
Cocksfoot mild mosaic virus
Cocksfoot mottle virus
Cocksfoot streak virus
Coconut foliar decay virus
Coleus vein necrosis virus
Colobus guereza papillomavirus
Colombian datura virus
Columbid circovirus
Common chimpanzee papillomavirus
Common marmoset foamy virus
Common midwife toad ranavirus
Common-moorhen coronavirus
Cordyline virus
Coronavirus
Cote d'Ivoire ebolavirus
Cotesia congregata virus
Cotia virus
Cotton leaf curl Burewala betasatellite
Cotton leaf curl Burewala virus
Cotton leaf curl Gezira alphasatellite
Cotton leaf curl Gezira virus
Cotton leaf curl Kokhran virus
Cotton leaf curl Multan betasatellite
Cotton leaf curl Multan virus
Cotton leaf curl Shadadpur virus
Cotton leafroll dwarf virus
Cottontail rabbit (Shope) papillomavirus
Cottontail rabbit papillomavirus
Cowpea aphid-borne mosaic virus
Cowpox virus
Coxsackievirus
Crassocephalum yellow vein virus
Crocuta crocuta papillomavirus
Croton yellow vein mosaic virus
Croton yellow vein virus
Crow polyomavirus
Cryphonectria hypovirus
Cryptophlebia leucotreta granulovirus
Cucumber fruit mottle mosaic virus
Cucumber green mottle mosaic virus
Cucumber mottle virus
Cucumber necrosis virus
Cucumber vein yellowing virus
Cucurbit aphid-borne yellows virus
Culex flavivirus
Culex nigripalpus baculovirus
Culex originated Tymoviridae-like virus
Culex tritaeniorhynchus rhabdovirus
Curtovirus
Cutthroat trout virus
Cycad leaf necrosis virus
Cyclovirus
Cydia pomonella granulovirus
Cygnus olor circovirus
Cymbidium mosaic virus
Cynomolgus macaque cytomegalovirus
Cyprinid herpesvirus
DG-75 Murine leukemia virus
Dahlia common mosaic virus
Dahlia mosaic virus
Daphne mosaic virus
Deer papillomavirus
Deerpox virus
Deformed wing virus
Delphinus delphis papillomavirus
Dendrolimus punctatus densovirus
Dengue Virus Type 2
Dengue type 3 virus
Dengue virus
Diaporthe ambigua RNA virus
Diascia yellow mottle virus
Diatraea saccharalis densovirus
Digitaria ciliaris striate mosaic virus
Digitaria didactyla striate mosaic virus
Digitaria streak virus
Dioscorea bacilliform virus
Diplodia scrobiculata RNA virus
Diuris virus
Dolichos yellow mosaic virus
Donggang virus
Dracaena mottle virus
Dragonfly circularisvirus
Dragonfly cyclicusvirus
Dragonfly cyclovirus
Dragonfly orbiculatusvirus
Dragonfly-associated circular virus
Dragonfly-associated mastrevirus
Drosophila A virus
Drosophila C virus
Drosophila melanogaster sigma virus
Drosophila melanogaster totivirus
Drosophila obscura sigma virus
Duck astrovirus
Duck circovirus
Duck coronavirus
Duck egg-drop syndrome virus
Duck enteritis virus
Duck flavivirus
Duck hepatitis A virus
Duck hepatitis B Virus DNA
Duck hepatitis B virus
Duck hepatitis virus
Duck picornavirus
Dulcamara mottle virus
Duvenhage virus
Dweet mottle virus
East African cassava mosaic virus
East Asian Passiflora virus
Eastern equine encephalitis virus
Ebola virus
Echovirus
Eclipta yellow vein virus
Ecotropis obliqua NPV
Ectocarpus siliculosus virus
Ectromelia virus
Eel Virus European X
Eidolon helvum parvovirus
Eidolon polyomavirus
Eimeria brunetti RNA virus
Elephant endotheliotropic herpesvirus
Elephantid herpesvirus
Eliat virus
Emilia yellow vein virus
Encephalomyocarditis (EMC) virus
Encephalomyocarditis virus
Entebbe bat virus
Enterovirus
Enzootic nasal tumour virus
Epinotia aporema granulovirus
Epiphyas postvittana nucleopolyhedrovirus
Epizootic haematopoietic necrosis virus
Epstein-Barr virus
Equid herpesvirus
Equine Pegivirus
Equine adenovirus
Equine arteritis virus
Equine coronavirus
Equine foamy virus
Equine herpesvirus
Equine infectious anemia virus
Equine papillomavirus
Equine polyomavirus
Equine rhinitis A virus
Equine rhinovirus
Equinus papillomavirus
Equus caballus papillomavirus
Equus ferus caballus papillomavirus
Eragrostis curvula streak virus
Eragrostis minor streak virus
Eragrostis streak virus
Erethizon dorsatum papillomavirus
Erysimum latent virus
Erythrovirus
Eupatorium vein clearing virus
Eupatorium yellow vein virus
Euphorbia leaf curl Guangxi virus
Euproctis pseudoconspersa nucleopolyhedrovirus
Euprosterna elaeasa virus
European bat lyssavirus
European elk papillomavirus
European hedgehog papillomavirus
European sheatfish virus
Farmington virus
Feldmannia species virus
Felid herpesvirus
Feline bocavirus
Feline calicivirus
Feline coronavirus
Feline foamy virus
Feline immunodeficiency virus
Feline infectious peritonitis virus
Feline leukemia virus
Feline morbillivirus
Feline papillomavirus
Feline picornavirus
Felis domesticus papillomavirus
Fenneropenaeus chinensis hepatopancreatic densovirus
Fer-de-lance virus
Ferret hepatitis E virus
Fig badnavirus
Fig fleck-associated virus
Finch circovirus
Finch polyomavirus
Flavivirus
Foot-and-mouth disease virus
Fort Morgan virus
Fowl adenovirus
Fowlpox virus
Foxtail mosaic virus
Francolinus leucoscepus papillomavirus
Frangipani mosaic virus
Freesia mosaic virus
French bean severe leaf curl virus
Friend murine leukemia virus
Friend spleen focus-forming virus
Fringilla coelebs papillomavirus
Frog adenovirus
Frog virus
Fujinami sarcoma virus
Furcraea necrotic streak virus
Fusarium graminearum dsRNA mycovirus
Fusarium graminearum hypovirus
Fusellovirus
GB virus
Galinsoga mosaic virus
Gallid herpesvirus
Gammapapillomavirus
Gammaretrovirus
Garlic common latent virus
Garlic virus
Gastropod associated circular ssDNA virus
Gayfeather mild mottle virus
Gentian Kobu-sho-associated virus
Geobacillus virus
Getah virus
Giardia canis virus
Giardia lamblia virus
Gibbon leukemia virus
Gill-associated virus
Giraffe coronavirus
Glomus sp. RF1 medium virus
Glossina pallidipes salivary gland hypertrophy virus
Goatpox virus
Goose adenovirus
Goose circovirus
Goose hemorrhagic polyomavirus
Goose paramyxovirus
Goose parvovirus
Gooseberry vein banding virus
Gorilla gorilla gorilla polyomavirus
Grapevine Pinot gris virus
Grapevine Rupestris stem pitting associated virus
Grapevine Rupestris stem pitting virus
Grapevine Syrah Virus-1
Grapevine berry inner necrosis virus
Grapevine endophyte endornavirus
Grapevine fleck virus
Grapevine geminivirus
Grapevine leafroll-associated virus
Grapevine rupestris stem pitting-associated virus
Grapevine vein-clearing virus
Grapevine virus
Grass carp rhabdovirus
Gremmeniella abietina RNA virus
Gremmeniella abietina mitochondrial RNA virus
Gremmeniella abietina type B RNA virus
Ground squirrel hepatitis virus
Grouper iridovirus
Gryllus bimaculatus nudivirus
Gull circovirus
Gyrovirus
HBV genotype A1
HBV genotype A2
HBV genotype B DNA
HBV genotype C DNA
HBV genotype D, serotype ayw3
HBV genotype D3
HBV genotype D4
HBV genotype E
HBV genotype F2
HBV genotype F4
HBV genotype G DNA
HBV genotype H DNA
HIV-1
HIV-1 92BR025 from Brazil
HIV-1 CRF03_AB
HIV-1 CRF04_cpx clone 94CY032-3 from Cyprus
HIV-1 E9 from the USA
HIV-1 G829 from Ghana
HIV-1 M_02CD.KS069 proviral
HIV-1 M_02CD.LBTB032 proviral
HIV-1 M_02CD.LBTB084 proviral
HIV-1 M_02CD.MBTB047 proviral
HIV-1 M_97CD.KFE267 proviral
HIV-1 M_97CD.KTB119 proviral
HIV-1 M_97CD.MBFE250 proviral
HIV-1 chimpanzee C455
HIV-1 chimpanzee C499
HIV-1 clone 00PTHDE10 from Portugal
HIV-1 clone 309 from China
HIV-1 clone 341 from China
HIV-1 clone 90cf402 from the Central African Republic
HIV-1 clone 92ug037 from Uganda
HIV-1 clone 93th253 from Thailand
HIV-1 clone 96TZ-BF061 from Tanzania
HIV-1 clone 96TZ-BF071 from Tanzania
HIV-1 clone 96TZ-BF110 from Tanzania
HIV-1 clone 98PTHEM103 from Portugal
HIV-1 clone C.96BW06.H51 from Botswana
HIV-1 clone C.96BW06.J4 from Botswana
HIV-1 clone C.96BW06.J7 from Botswana
HIV-1 clone C.96BW06.K18 from Botswana
HIV-1 clone C1P from USA
HIV-1 clone D24 from India
HIV-1 clone ES1-16 from USA
HIV-1 clone ES1-20 from USA
HIV-1 clone ES10-53 from USA
HIV-1 clone ES4-24 from USA
HIV-1 clone ES8-17 from USA
HIV-1 clone ES8-43 from USA
HIV-1 clone I-1 from USA
HIV-1 clone I-2 from USA
HIV-1 clone MJ4 from Botswana
HIV-1 clone N-1 from USA
HIV-1 clone N-2 from USA
HIV-1 clone S61D1 from Spain
HIV-1 clone S61D15 from Spain
HIV-1 clone S61G1 from Spain
HIV-1 clone S61G7 from Spain
HIV-1 clone XJDC6431-2 from China
HIV-1 clone XJDC6441 from China
HIV-1 clone XJN0084 from China
HIV-1 clone ZAM184-5.6 from Zambia
HIV-1 clone p05MYKL045.1 from Malaysia
HIV-1 clone pBD6.15 from Cameroon
HIV-1 clone pCM235-2 from Thailand
HIV-1 clone pCM235-4 from USA
HIV-1 clone pCMO2.3 from Cameroon
HIV-1 clone pCMO2.5 from Cameroon
HIV-1 clone pIIIB from USA
HIV-1 clone pWCML249 from Kenya
HIV-1 clone pZAC from South Africa
HIV-1 genotype CRF05_DF
HIV-1 patient WCIPR sample 1985 clone 4
HIV-1 patient WCIPR sample 1985 clone 46
HIV-1 patient WCIPR sample 1985 clone 5
HIV-1 patient WCIPR sample 1985 clone 52
HIV-1 patient WCIPR sample 1985 clone 54
HIV-1 patient WCIPR sample 1990 clone 11
HIV-1 patient WCIPR sample 1990 clone 18
HIV-2
HIV-l from Greece
HMO Astrovirus
HPIV-1
Halastavi arva RNA virus
Haloarcula hispanica icosahedral virus
Haloarcula hispanica pleomorphic virus
Halogeometricum pleomorphic virus
Halorubrum pleomorphic virus
Halovirus
Hamster polyomavirus
Hana virus
Hardenbergia mosaic virus
Hardenbergia virus
Helicobasidium mompa endornavirus
Helicoverpa armigera NPV
Helicoverpa armigera NPV NNg1 DNA
Helicoverpa armigera densovirus
Helicoverpa armigera granulovirus
Helicoverpa armigera multiple nucleopolyhedrovirus
Helicoverpa armigera nuclear polyhedrosis virus
Helicoverpa zea nudivirus
Helicoverpa zea single nucleocapsid nucleopolyhedrovirus
Heliocoverpa armigera nucleopolyhedrovirus
Heliothis virescens ascovirus
Heliothis zea virus
Helleborus net necrosis virus
Hemorrhagic enteritis virus
Hendra virus
Hepataitis E virus
Hepatitis A virus
Hepatitis B Virus
Hepatitis B virus
Hepatitis C virus
Hepatitis D Virus genotype 3, clone 010-OBC Cl11
Hepatitis D Virus genotype 3, clone 010-OBCCl2
Hepatitis D Virus genotype 3, clone BR2-ENB
Hepatitis D virus
Hepatitis E virus
Hepatitis G virus
Hepatitis GB virus
Hepatitis delta virus
Hepatopancreatic parvovirus
Heron hepatitis B virus
Herpes simplex virus
Heterocapsa circularisquama RNA virus
Heterosigma akashiwo RNA virus
Hibiscus chlorotic ringspot virus
Hibiscus latent Singapore virus
Highlands J virus
Hippeastrum mosaic virus
Hipposideros bat coronavirus
Hirame rhabdovirus
His1 virus
His2 virus
Hog cholera virus
Hollyhock leaf crumple virus
Hollyhock leaf curl virus
Hollyhock yellow vein mosaic virus
Homalodisca coagulata virus
Honeysuckle ringspot virus
Honeysuckle yellow vein Kagoshima virus
Honeysuckle yellow vein beta-[Japan:Fukui:2001] DNA
Honeysuckle yellow vein beta-[Japan:Masuda:2003] DNA
Honeysuckle yellow vein mosaic beta-[Japan:Kumamoto:1998] DNA
Honeysuckle yellow vein mosaic beta-[Japan:Miyizaki:2001] DNA
Honeysuckle yellow vein mosaic disease associated satellite DNA beta-[Ibaraki] DNA
Honeysuckle yellow vein mosaic disease associated satellite DNA beta-[Nara] DNA
Honeysuckle yellow vein mosaic virus
Honeysuckle yellow vein virus
Hop latent virus
Hop mosaic virus
Hordeum mosaic virus
Horsepox virus
Horseradish latent virus
Huma Immunodeficiency Virus Isolate D205
Human Bocavirus
Human Coronavirus
Human JC virus
Human Papillomavirus
Human Respiratory syncytial virus
Human T Cell Lymphotropic Virus I
Human T-cell lymphotropic virus
Human T-lymphotropic virus
Human TMEV-like cardiovirus
Human adenovirus
Human astrovirus
Human betacoronavirus
Human bocavirus
Human calicivirus
Human circular dsDNA virus
Human coronavirus
Human coxsackievirus
Human cytomegalovirus
Human echovirus
Human endogenous retrovirus
Human enteric coronavirus
Human enterovirus
Human foamy virus
Human group 1 coronavirus
Human gyrovirus
Human hepatitis A virus
Human hepatitis virus
Human herpesvirus
Human immunodeficiency virus
Human lymphadenopathy virus
Human metapneumovirus
Human papillomavirus
Human papillomoavirus
Human parainfluenza virus
Human parechovirus
Human parvovirus
Human poliovirus
Human polyomavirus
Human respiratory syncytial virus
Human rhinovirus
Human spumaretrovirus
Hybrid snakehead virus
Hydrangea chlorotic mottle virus
Hydrangea ringspot virus
Hyperthermophilic Archaeal Virus 1
Hyperthermophilic Archaeal Virus 2
Hyphantria cunea nucleopolyhedrovirus
Ia io picornavirus
Ictalurid herpesvirus
Igbo Ora virus
Iguape virus
Ikoma lyssavirus
Ilheus virus
Indian cassava mosaic virus
Indian citrus ringspot virus
Infectious bronchitis virus
Infectious flacherie virus
Infectious haematopoietic necrosis virus
Infectious hematopoietic necrosis virus
Infectious hypodermal and hematopoietic necrosis virus
Infectious spleen and kidney necrosis virus
Influenza A virus
Ipomoea yellow vein virus
Iranian johnsongrass mosaic virus
Iranian maize mosaic nucleorhabdovirus
Irkut virus
Israel acute paralysis virus
Israeli acute paralysis virus
J-virus
JC polyomavirus
JC virus
Jaagsiekte sheep retrovirus
Japanese encephalitis SA-14 virus
Japanese encephalitis virus
Japanese iris necrotic ring virus
Japanese yam mosaic virus
Jembrana disease virus
Jurona virus
KI polyomavirus
Kakugo virus
Kalanchoe latent virus
Kalanchoe top-spotting virus
Karshi virus
Kashmir bee virus
Kedougou virus
Kelp fly virus
Kennedya yellow mosaic virus
Keunjorong mosaic virus
Khujand lyssavirus
Kimberley virus
Koala retrovirus
Kobuvirus
Koi herpesvirus
Kokobera virus
Konjac mosaic virus
Kotonkan virus
Kyasanur forest disease virus
Kyuri green mottle mosaic virus
Lactate dehydrogenase-elevating virus
Lagenorhynchus acutus papillomavirus
Lagos bat virus
Lake Victoria marburgvirus
Lamium leaf distortion associated virus
Langat virus
Large yellow croaker iridovirus
Lausannevirus
Leek yellow stripe virus
Leishmania RNA virus
Lelystad virus
Leporid herpesvirus
Lettuce necrotic yellows virus
Lettuce virus
Lettuce yellow mottle virus
Leucania separata nuclear polyhedrosis virus
Ligustrum necrotic ringspot virus
Lily mottle virus
Lily symptomless virus
Lisianthus necrosis virus
Little cherry virus
Ljungan virus
Lloviu virus
Lolium latent virus
Lordsdale virus
Louping ill virus
Lucerne transient streak virus
Lucky bamboo bacilliform virus
Ludwigia yellow vein virus
Lumpy skin disease virus
Lupine mosaic virus
Lygus lineolaris virus
Lymantria dispar nucleopolyhedrovirus
Lymantria xylina MNPV
Lymphocystis disease virus
Lynx rufus papillomavirus
Lyssavirus
MW polyomavirus
Macaca fascicularis papillomavirus
Macaca fascicularis polyomavirus
Macaca fuscata rhadinovirus
Macaca mulatta rhadinovirus
Macacine herpesvirus
Macaque simian foamy virus
Macrobrachium rosenbergii Taihu virus
Magnaporthe oryzae virus
Magpie-robin coronavirus
Maize chlorotic dwarf virus
Maize chlorotic mottle virus
Maize dwarf mosaic virus
Maize fine streak virus
Maize mosaic virus
Maize necrotic streak virus
Maize rayado fino virus
Maize streak Reunion virus
Maize streak virus
Maize white line mosaic virus
Malakal virus
Malpais Spring virus
Malvastrum leaf curl Guangdong virus
Malvastrum yellow mosaic virus
Malvastrum yellow vein Yunnan virus
Malvastrum yellow vein virus
Mamastrovirus
Mamestra brassicae MNPV
Mamestra brassicae multiple nucleopolyhedrovirus
Mamestra configurata NPV-A
Mamestra configurata nucleopolyhedrovirus
Mapuera virus
Maraba virus
Maracuja mosaic virus
Marburg marburgvirus
Marine RNA virus
Marseillevirus
Maruca vitrata MNPV
Mason-Pfizer monkey virus
Mastadenovirus
Mastomys coucha papillomavirus
Mastomys polyomavirus
Mavirus
Mayaro virus
Measles virus
Megavirus
Melanoplus sanguinipes entomopoxvirus
Meleagrid herpesvirus
Melon aphid-borne yellows virus
Melon necrotic spot virus
Menangle virus
Mengo virus
Meno virus
Merkel cell polyomavirus
Mesta yellow vein mosaic virus
Micro Torque teno virus
Micromonas sp. RCC1109 virus
Microvirus
Midway virus
Miniopterus polyomavirus
Miniopterus schreibersii papillomavirus
Miniopterus schreibersii picornavirus
Mink astrovirus
Mink calicivirus
Mink coronavirus
Mint virus
Minute virus
Mirabilis jalapa mottle virus
Mirabilis mosaic virus
Miscanthus streak virus
Mokola virus
Molluscum contagiosum virus
Moloney murine leukemia virus
Moloney murine sarcoma virus
Monkey B-lymphotropic papovavirus
Monkeypox virus
Morelia spilota papillomavirus
Moroccan watermelon mosaic virus
Mosquito VEM Anellovirus
Mosquito VEM virus
Mosquito densovirus
Mosquito flavivirus
Mossman virus
Mouse astrovirus
Mouse hepatitis virus
Mouse kobuvirus
Mouse parvovirus
Mouse polyomavirus
Moussa virus
MuLV
Mud crab dicistrovirus
Mulard duck circovirus
Mumps virus
Mungbean yellow mosaic India virus
Mungbean yellow mosaic virus
Munia coronavirus
Murid herpesvirus
Murine adenovirus
Murine astrovirus
Murine coronavirus
Murine cytomegalovirus
Murine hepatitis virus
Murine herpesvirus
Murine leukemia virus
Murine norovirus
Murine osteosarcoma virus
Murine pneumotropic virus
Murine polyomavirus
Muromegalovirus
Murray Valley encephalitis virus
Mus dunni endogenous virus
Mus musculus papillomavirus
Musca domestica salivary gland hypertrophy virus
Muscovy duck circovirus
Muscovy duck parvovirus
Mutant Porcine reproductive and respiratory syndrome virus
Mutant Rabies virus
Mycoplasma virus
Myotis myotis bocavirus
Myotis polyomavirus
Myotis ricketti papillomavirus
Mythimna loreyi densovirus
Mythimna separata entomopoxvirus
Myxoma virus
Nam Dinh virus
Nandina mosaic virus
Nanovirus
Narcissus common latent virus
Narcissus degeneration virus
Narcissus late season yellows virus
Narcissus mosaic virus
Narcissus symptomless virus
Nariva virus
Ndumu virus
Nebovirus
Neodiprion abietis nucleopolyhedrovirus
Neodiprion lecontei NPV
Neodiprion sertifer nucleopolyhdrovirus
Nepavirus
Nerine latent virus
Nerine virus
New World begomovirus
Newbury agent 1
Newcastle Disease virus
Newcastle disease virus
Ngaingan virus
Ngewotan virus
Night-heron coronavirus
Nilaparvata lugens honeydew virus
Nile crocodilepox virus
Niminivirus
Nipah virus
Nootka lupine vein-clearing virus
Nora virus
Norovirus
Northern cereal mosaic virus
Norwalk virus
Norwalk-like virus
Nse virus
Ntaya virus
Nudaurelia capensis beta virus
Nyamanini virus
O'Nyong-nyong virus
O'nyong-nyong virus
Oak-Vale virus
Oat blue dwarf virus
Oat dwarf virus
Oat golden stripe virus
Oat necrotic mottle virus
Obodhiang virus
Ockelbo virus
Odontoglossum ringspot virus
Okra leaf curl Mali virus
Okra leaf curl virus
Okra mosaic virus
Okra yellow crinkle Cameroon alphasatellite
Okra yellow crinkle virus
Old World harvest mouse papillomavirus
Olive latent virus
Olive mild mosaic virus
Omsk hemorrhagic fever virus
Onion yellow dwarf virus
Ononis yellow mosaic virus
Orange-spotted grouper iridovirus
Orangutan hepadnavirus
Orangutan polyomavirus
Orf virus
Orgyia leucostigma NPV
Orgyia pseudotsugata MNPV
Ornithogalum mosaic virus
Oryctes rhinoceros virus
Oryza rufipogon endornavirus
Oryza sativa endornavirus
Ostreid herpesvirus
Ostreococcus lucimarinus virus
Ostreococcus tauri virus
Ostreococcus virus
Otomops polyomavirus
Ovine adenovirus
Ovine enterovirus
Ovine enzootic nasal tumor virus
Ovine herpesvirus
Ovine hungarovirus
Ovine lentivirus
Ovine papillomavirus
Ovine pulmonary adenocarcinoma virus
Oyster mushroom spherical virus
PRCV ISU-1
PRRSV HB-1(sh)/2002
PRRSV HB-2(sh)/2002
PRRSV LV4.2.1
Pan troglodytes schweinfurthii polyomavirus
Pan troglodytes verus polyomavirus
Panax virus
Panicum streak virus
Panine herpesvirus
Panthera leo persica papillomavirus
Papaya leaf crumple virus
Papaya leaf curl China virus
Papaya leaf curl Guangdong virus
Papaya leaf curl virus
Papaya leaf distortion mosaic virus
Papaya mosaic virus
Papaya ringspot virus
Papilio polyxenes densovirus
Papillomavirus
Papio hamadryas papillomavirus
Paprika mild mottle virus
Parainfluenza virus
Paralichthys olivaceus rhabdovirus
Paramecium bursaria Chlorella virus
Parechovirus
Parrot hepatitis B virus
Parvovirus
Paspalum dilatatum striate mosaic virus
Paspalum striate mosaic virus
Passiflora latent carlavirus
Passion fruit mosaic virus
Passion fruit woodiness virus
Pea seed-borne mosaic virus
Pea stem necrosis virus
Peace lily mosaic virus
Peach chlorotic mottle virus
Peanut chlorotic streak caulimovirus
Peanut mottle virus
Peanut stripe virus
Peanut stunt virus
Pedilanthus leaf curl virus
Pedilathus leaf curl virus
Pelargonium chlorotic ring pattern virus
Pelargonium flower break carmovirus
Pelargonium flower break virus
Pelargonium line pattern virus
Pelargonium necrotic spot virus
Pelargonium vein banding virus
Penaeid shrimp infectious myonecrosis virus
Penaeus merguiensis densovirus
Penaeus monodon hepatopancreatic parvovirus
Pennisetum mosaic virus
Pepino mosaic virus
Pepper curly top virus
Pepper leaf curl Lahore virus
Pepper leaf curl Yunnan virus
Pepper leaf curl virus
Pepper mild mottle virus
Pepper mottle virus
Pepper severe mosaic virus
Pepper vein yellows virus
Pepper veinal mottle virus
Pepper yellow dwarf virus
Pepper yellow leaf curl China virus
Pepper yellow leaf curl Indonesia virus
Pepper yellow leaf curl virus
Pepper yellow mosaic virus
Pepper yellow vein Mali virus
Perina nuda picorna-like virus
Perinet virus
Periplaneta fuliginosa densovirus
Peromyscus papillomavirus
Persea americana endornavirus
Persimmon cryptic virus
Peru tomato mosaic virus
Peste des petits ruminants virus
Peste-des-petits-ruminants virus
Pestivirus
Petunia vein clearing virus
Phaeocystis globosa virus
Phaius virus
Philosamia cynthia ricini nucleopolyhedrovirus
Phlox Virus B
Phlox virus
Phocine distemper virus
Phocoena phocoena papillomavirus
Phocoena spinipinnis papillomavirus
Phthorimaea operculella granulovirus
Phytophthora infestans RNA virus
Picalivirus
Picobiliphyte sp. MS584-5 nanovirus
Pieris rapae granulovirus
Pig stool associated circular ssDNA virus
Pigeon paramyxovirus
Pigeon picornavirus
Pike fry rhabdovirus
Piliocolobus rufomitratus polyomavirus
Pine marten torque teno virus
Pineapple bacilliform comosus virus
Pineapple mealybug wilt-associated virus
Piscine myocarditis virus
Plantago asiatica mosaic virus
Plum bark necrosis and stem pitting-associated virus
Plum pox virus
Plutella xylostella multiple nucleopolyhedrovirus
Pneumonia virus
Po-Circo-like virus
Poinsettia mosaic virus
Pokeweed mosaic virus
Poliovirus
Polyomavirus
Poplar mosaic virus
Porcine TTV 2 from China
Porcine adenovirus
Porcine associated stool circular virus
Porcine astrovirus
Porcine bocavirus
Porcine circovirus
Porcine circovius type 2
Porcine coronavirus
Porcine endogenous retrovirus
Porcine endogenous type C retrovirus
Porcine enteric calicivirus
Porcine enteric sapovirus
Porcine enterovirus
Porcine epidemic diarrhea virus
Porcine hemagglutinating encephalomyelitis virus
Porcine kobuvirus
Porcine parvovirus
Porcine reproductive and respiratory syndrome virus
Porcine respiratory and reproductive syndrome virus
Porcine sapelovirus
Porcine teschovirus
Posavirus
Possum enterovirus
Potato Virus P from Brazil
Potato apical leaf curl disease-associated satellite DNA beta
Potato latent virus
Potato leafroll virus
Potato mop-top virus
Potato rough dwarf virus
Potato virus
Potato yellow dwarf virus
Potato yellow mosaic virus
Pothos latent virus
Powassan virus
Procyon lotor papillomavirus
Providence virus
Pseudaletia unipuncta granulovirus
Pseudocowpox virus
Pseudoplusia includens densovirus
Psittacid herpesvirus
Psittacus erithacus timneh papillomavirus
Pteronotus polyomavirus
Puma concolor papillomavirus
Pyrobaculum spherical virus
Pyrococcus abyssi virus
Quail picornavirus
Quang Binh virus
RD114 retrovirus
RHDV-BS89
RHDV-SD
Rabbit astrovirus
Rabbit calicivirus
Rabbit coronavirus
Rabbit fibroma virus
Rabbit hemorrhagic disease virus
Rabbit oral papillomavirus
Rabbitpox virus
Rabies virus
Raccoon polyomavirus
Rachiplusia ou multiple nucleopolyhedrovirus
Radish leaf curl virus
Rana grylio iridovirus
Ranid herpesvirus
Raptor adenovirus
Raspberry leaf blotch virus
Raspberry mottle virus
Rat coronavirus
Rat cytomegalovirus
Rat parvovirus
Rat theilovirus
Rattail cactus necrosis associated virus
Rattus norvegicus papillomavirus
Rauscher murine leukemia virus
Raven circovirus
Recombinant Hepatitis C Virus SA13/JFH1
Recombinant Hepatitis C virus
Recombinant chimeric Hepatitis C virus
Recombinant vesicular stomatitis Indiana virus
Red clover vein mosaic virus
Rehmannia mosaic virus
Reindeer papillomavirus
Reptile vesivirus
Respiratory syncytial virus
Reston Ebola virus
Reston ebolavirus
Reticuloendotheliosis virus
Retroviridae
Rhesus cytomegalovirus
Rhesus papillomavirus
Rhinolophus ferrumequinum circovirus
Rhododendron virus
Rhopalosiphum padi virus
Rhynchosia yellow mosaic virus
Ribgrass mosaic virus
Rice tungro bacilliform virus
Rice tungro spherical virus
Rice yellow mottle virus
Rice yellow stunt virus
Rinderpest virus
Rio Bravo virus
Rocio virus
Rock bream iridovirus
Rodent hepacivirus
Rodent herpesvirus
Rodent pegivirus
Rodent stool-associated circular genome virus
Rosa rugosa leaf distortion virus
Rose leaf curl virus
Rose spring dwarf-associated virus
Rose yellow vein virus
Rosellinia necatrix partitivirus
Ross River virus
Ross' goose hepatitis B virus
Rosy apple aphid virus
Rous sarcoma virus
Rousettus aegyptiacus papillomavirus
Rousettus bat coronavirus
Rubella virus
Rubus canadensis virus
Rudbeckia flower distortion virus
Rupestris stem pitting associated virus
Rupestris stem pitting-associated virus
Ryegrass mosaic virus
SARS Coronavirus
SARS coronavirus
SIVcpz proviral
STL polyomavirus
Sable antelope coronavirus
Sacbrood virus
Saccharomyces cerevisiae killer virus
Saccharum streak virus
Saffold virus
Sagiyama virus
Saguaro cactus virus
Saimiri sciureus polyomavirus
Saimiriine herpesvirus
Salem virus
Salivirus
Salmon pancreas disease virus
Salmonid alphavirus
Sambar deer coronavirus
San Miguel sea lion virus
Sapovirus
Scallion mosaic virus
Scallion virus
Scheffersomyces segobiensis virus
Schlumbergera virus
Sclerotinia sclerotiorum debilitation-associated RNA virus
Sclerotinia sclerotiorum dsRNA mycovirus
Sclerotinia sclerotiorum hypovirus
Sclerotinia sclerotiorum mitovirus
Scophthalmus maximus rhabdovirus
Sea Turtle Tornovirus
Seal picornavirus
Semliki forest virus
Sendai virus
Seneca valley virus
Sepik virus
Sesbania mosaic virus
Shallot latent virus
Shallot virus
Shallot yellow stripe virus
Sheep astrovirus
Sheeppox virus
Sheldgoose hepatitis B virus
Shimoni bat virus
Shrimp white spot syndrome virus
Sibine fusca densovirus
Sida golden mosaic Buckup virus
Sida golden mosaic Florida virus
Sida golden mosaic virus
Sida golden yellow vein virus
Sida leaf curl virus
Sida micrantha mosaic virus
Sida yellow vein Madurai virus
Siegesbeckia yellow vein virus
Silurus glanis circovirus
Simian (African green monkey) immunodeficiency virus
Simian (macaque) immunodeficiency virus
Simian (stump-tailed macaque) immunodeficiency virus
Simian Agent 10
Simian Mason-Pfizer D-type retrovirus
Simian SRV-1 type D retrovirus
Simian T-cell lymphotropic virus
Simian T-lymphotropic virus
Simian adenovirus
Simian agent 12
Simian agent 5
Simian endogenous retrovirus
Simian enterovirus
Simian foamy virus
Simian hemorrhagic fever virus
Simian hepatitis A virus
Simian immunodeficiency PBJ virus
Simian immunodeficiency virus
Simian retrovirus
Simian sapelovirus
Simian virus
Simian-Human immunodeficiency virus
Sindbis virus
Sindbis-like virus
Singapore grouper iridovirus
Siniperca chuatsi rhabdovirus
Sitiawan virus
Sleeping disease virus
Small anellovirus
Small ruminant lentivirus
Snake adenovirus
Snakehead retrovirus
Snakehead rhabdovirus
Snow Mountain virus
Snow goose hepatitis B virus
Soft-shelled turtle iridovirus
Soil-borne cereal mosaic virus
Soil-borne wheat mosaic virus
Solenopsis invicta virus
Sonchus yellow net virus
Sorghum mosaic virus
Sour cherry green ring mottle virus
South African cassava mosaic virus
South polar skua adenovirus
Southern bean mosaic virus
Southern cowpea mosaic virus
Southern elephant seal virus
Southern tomato virus
Sowbane mosaic virus
Soybean chlorotic blotch virus
Soybean crinkle leaf virus
Soybean dwarf virus
Soybean mild mottle pararetrovirus
Soybean mild mottle virus
Soybean mosaic virus
Soybean yellow common mosaic virus
Soybean yellow mottle mosaic virus
Sparrow coronavirus
Sphaeropsis sapinea RNA virus
Spider monkey foamy virus
Spinach curly top Arizona virus
Spinach curly top virus
Spinach severe curly top virus
Spiroplasma kunkelii virus
Spissistilus festinus virus
Spodoptera exigua Iflavirus
Spodoptera exigua iflavirus
Spodoptera exigua nucleopolyhedrovirus
Spodoptera frugiperda MNPV
Spodoptera frugiperda MNPV genotype SfMNPV-G defective
Spodoptera frugiperda ascovirus
Spodoptera littoralis NPV
Spodoptera litura granulovirus
Spodoptera litura nucleopolyhedrovirus
Sporobolus striate mosaic virus
Spring Viremia of Carp
Spring viraemia of carp virus
Spring viremia of carp virus
Squash leaf curl China virus
Squash leaf curl Philippines virus
Squash leaf curl Yunnan virus
Squash vein yellowing virus
Squirrel monkey foamy virus
Squirrel monkey polyomavirus
Squirrel monkey retrovirus
Sri Lankan cassava mosaic virus
St. Louis encephalitis virus
Stachytarpheta leaf curl virus
Starling circovirus
Steller sea lion vesivirus
Stork hepatitis B virus
Strawberry chlorotic fleck associated virus
Strawberry vein banding virus
Streptocarpus flower break virus
Suakwa aphid-borne yellows virus
Subterranean clover mottle virus
Sudan ebolavirus
Sugarcane bacilliform IM virus
Sugarcane bacilliform virus
Sugarcane mosaic virus
Sugarcane streak Egypt virus
Sugarcane streak Reunion virus
Sugarcane streak mosaic virus
Sugarcane streak virus
Sugarcane yellow leaf virus
Suid herpesvirus
Sulfolobales Mexican fusellovirus
Sulfolobales Mexican rudivirus
Sulfolobus islandicus rudivirus
Sulfolobus spindle-shaped virus
Sulfolobus tengchongensis spindle-shaped virus
Sulfolobus turreted icosahedral virus
Sulfolobus virus
Sunflower chlorotic mottle virus
Sunflower mild mosaic virus
Sunshine virus
Sus scrofa papillomavirus
Sweet potato begomovirus
Sweet potato caulimo-like virus
Sweet potato chlorotic fleck virus
Sweet potato feathery mottle virus
Sweet potato geminivirus
Sweet potato golden vein associated virus
Sweet potato latent virus
Sweet potato leaf curl Canary Island virus
Sweet potato leaf curl Canary virus
Sweet potato leaf curl China Sichuan virus
Sweet potato leaf curl China virus
Sweet potato leaf curl Georgia virus
Sweet potato leaf curl Korean virus
Sweet potato leaf curl Lanzarote virus
Sweet potato leaf curl Sao Paulo virus
Sweet potato leaf curl South Carolina virus
Sweet potato leaf curl Spain virus
Sweet potato leaf curl Uganda virus
Sweet potato leaf curl virus
Sweet potato mosaic associated virus
Sweet potato vein clearing virus
Sweet potato virus
Sweetpotato badnavirus
Swine hepatitis E virus
Swine parainfluenza virus
Swine pasivirus
Swine vesicular disease virus
Swinepox virus
Switchgrass mosaic virus
TGEV Miller M6
TGEV Miller M60
TGEV Purdue P115
TGEV virulent Purdue
TPA_exp: Aeropyrum pernix ovoid virus
TPA_exp: Aeropyrum pernix spindle-shaped virus
TPA_exp: Suid herpesvirus
TPA_inf: Human herpesvirus
TPA_inf: Porcine rubulavirus
TT virus
TTV-like mini virus
TYLCAxV-Sic1-[IT:Sic2/2:04]
TYLCAxV-Sic2-[IT:Sic2/5:04]
TYLCCNV-[Y322] satellite DNA beta sequence
Tailam virus
Tamana bat virus
Tamus red mosaic virus
Tanapox virus
Taro bacilliform virus
Taro vein chlorosis virus
Taterapox virus
Taura syndrome virus
Telosma mosaic virus
Tembusu virus
Tench rhabdovirus
Theiler murine encephalomyelitis
Theiler murine encephalomyelitis virus
Theiler's disease-associated virus
Theiler's encephalomyelitis virus
Theiler's murine encephalomyelitis virus
Theiler's-like virus
Theilers murine encephalomyelitis virus
Thermococcus prieurii virus
Thermoproteus tenax spherical virus
Thrush coronavirus
Thunberg fritillary virus
Thysanoplusia orichalcea NPV
Tianjin totivirus
Tibrogargan virus
Tick-borne encephalitis virus
Tiger frog virus
Tioman virus
Titi monkey adenovirus
Tobacco bushy top virus
Tobacco curly shoot virus
Tobacco etch virus
Tobacco leaf curl Japan virus
Tobacco leaf curl Kochi virus
Tobacco leaf curl Thailand virus
Tobacco leaf curl Yunnan virus
Tobacco leaf curl Zimbabwe virus
Tobacco leaf curl virus
Tobacco mild green mosaic virus
Tobacco mosaic virus
Tobacco necrosis virus
Tobacco rattle virus
Tobacco vein banding mosaic virus
Tobacco vein distorting virus
Tobacco vein-clearing virus
Tomato Chino La Paz virus
Tomato Yellow Leaf Curl Virus
Tomato begomovirus
Tomato bushy stunt virus
Tomato golden mosaic virus
Tomato leaf curl Bangalore virus
Tomato leaf curl Bangladesh virus
Tomato leaf curl China virus
Tomato leaf curl Comoros virus
Tomato leaf curl Cotabato virus
Tomato leaf curl Guangxi virus
Tomato leaf curl Gujarat virus
Tomato leaf curl Hainan virus
Tomato leaf curl Iran virus
Tomato leaf curl Java virus
Tomato leaf curl Karnataka alphasatellite
Tomato leaf curl Karnataka virus
Tomato leaf curl Laos virus
Tomato leaf curl Madagascar virus
Tomato leaf curl Mayotte virus
Tomato leaf curl Mindanao virus
Tomato leaf curl Namakely virus
Tomato leaf curl New Delhi virus
Tomato leaf curl Oman virus
Tomato leaf curl Pakistan virus
Tomato leaf curl Palampur virus
Tomato leaf curl Philippine virus
Tomato leaf curl Philippines virus
Tomato leaf curl Pune virus
Tomato leaf curl Ranchi betasatellite
Tomato leaf curl Ranchi virus
Tomato leaf curl Seychelles virus
Tomato leaf curl Sudan virus
Tomato leaf curl Taiwan virus
Tomato leaf curl Vietnam virus
Tomato leaf curl geminivirus
Tomato leaf curl virus
Tomato mosaic virus
Tomato necrotic stunt virus
Tomato yellow blotch virus
Tomato yellow dwarf disease associated satellite DNA beta-[Kochi] DNA
Tomato yellow leaf curl Axarquia virus
Tomato yellow leaf curl China virus
Tomato yellow leaf curl Malaga virus
Tomato yellow leaf curl Mali virus
Tomato yellow leaf curl Sardinia virus
Tomato yellow leaf curl Thailand betasatellite
Tomato yellow leaf curl Thailand virus
Tomato yellow leaf curl Vietnam virus
Tomato yellow leaf curl virus
Torque teno canis virus
Torque teno douroucouli virus
Torque teno felis virus
Torque teno midi virus
Torque teno mini virus
Torque teno sus virus
Torque teno tamarin virus
Torque teno virus
Transmissible gastroenteritis virus
Tree shrew adenovirus
Trichechus manatus latirostris papillomavirus
Trichodysplasia spinulosa-associated polyomavirus
Trichomonas vaginalis virus
Trichoplusia ni ascovirus
Trichoplusia ni single nucleopolyhedrovirus
Triticum mosaic virus
Tuber aestivum endornavirus
Tuber aestivum mitovirus
Tuhoko virus
Tulip virus
Tupaia herpesvirus
Tupaia paramyxovirus
Tupaia rhabdovirus
Turbot reddish body iridovirus
Turdivirus
Turkey adenovirus
Turkey astrovirus
Turkey avisivirus
Turkey coronavirus
Turkey gallivirus
Turkey parvovirus
Turnip crinkle virus
Turnip curly top virus
Turnip mosaic virus
Turnip rosette virus
Turnip vein-clearing virus
Turnip yellow mosaic Blue Lake
Turnip yellow mosaic virus
Turnip yellows virus
Tursiops truncatus papillomavirus
UR2 sarcoma virus
Ugandan cassava brown streak virus
Uncia uncia papillomavirus
Uncultured Microviridae clone SARssphi1
Uncultured Microviridae clone SARssphi2
Uncultured virus
Urochloa streak virus
Ursus maritimus papillomavirus
Ustilaginoidea virens RNA virus
Usutu virus
VESV-like calicivirus
Vaccinia virus
Vallota speciosa virus
Valsa ceratosperma hypovirus
Variola major virus
Variola minor virus
Variola virus
Varroa destructor virus
Velvet bean severe mosaic virus
Velvet tobacco mottle virus
Venezuelan equine encephalitis virus
Verbena virus
Vervet monkey polyomavirus
Vesicular exanthema of swine virus
Vesicular stomatitis Alagoas virus
Vesicular stomatitis Indiana virus
Vesicular stomatitis New Jersey virus
Viral haemorrhagic septicaemia virus
Viral hemorrhagic septicemia virus
Virus PhiCh1
Visna virus
Visna/Maedi virus
Visna/maedi virus
WU Polyomavirus
Walleye dermal sarcoma virus
Wasabi mottle virus
Waterbuck coronavirus
Watermelon bud necrosis virus
Watermelon mosaic virus
Wesselsbron virus
West Caucasian bat virus
West Nile virus
Western equine encephalomyelitis virus
Western roedeer papillomavirus
Wets NIle virus
Whataroa virus
Wheat dwarf India virus
Wheat dwarf virus
Wheat eqlid mosaic virus
Wheat streak mosaic virus
Wheat yellow dwarf virus
White bream virus
White spot syndrome virus
White-eye coronavirus
White-tailed deer coronavirus
Whitefly VEM 1 begomovirus
Whitefly VEM 2 begomovirus
Whitefly VEM satellite
Wigeon coronavirus
Wild potato mosaic virus
Wild tomato mosaic virus
Wiseana iridescent virus
Wisteria vein mosaic virus
Wongabel virus
Wood mouse herpesvirus
Woodchuck hepatitis B virus
Woodchuck hepatitis virus
Woolly monkey hepatitis B Virus
Woolly monkey hepatitis B virus
Xenotropic MuLV-related virus
Xenotropic murine leukemia virus
Y73 sarcoma virus
Yaba monkey tumor virus
Yaba-like disease virus
Yam bean mosaic virus
Yam mild mosaic virus
Yellow baboon polyomavirus
Yellow fever virus
Yellow head virus
Yoka poxvirus
Yokose virus
Youcai mosaic virus
Yug Bogdanovac virus
Zaire Ebola virus
Zaire ebolavirus
Zalophus californianus papillomavirus
Zantedeschia mild mosaic virus
Zika virus
Zucchini green mottle mosaic virus
Zucchini yellow mosaic virus
bat SARS coronavirus
coxsackievirus
ovine papillomavirus
pea seed-borne mosaic virus
sugarcane yellow leaf virus
variola minor virus
yellow vein China virus

jQuery-when2 – a more flexible way to work with jQuery deferreds

July 27th, 2013

I’ve often been frustrated by the limited functionality of jQuery.when. You give it some deferred objects and it fires when all the deferreds are finished. The trouble is, if any of the deferreds is rejected the deferred returned by jQuery.when fires immediately. So you can’t use it to collect the results of all deferreds including any errors. You also can’t use it to fire when the first of the passed deferreds fires.

So last night I wrote a new version of when, called jQuery-when2 that offers the three behaviors that I commonly want:

  1. resolve on the first success,
  2. fail on the first error (the jQuery.when behavior), or
  3. resolve when all results (successes or errors) have been collected.

The API differences from jQuery.when:

  • when2 must be called with a list as its first argument.
  • An options Javascript object may be passed as a second argument.
  • If options.resolveOnFirstSuccess is true, the deferred returned by when2 will resolve as soon as any of the passed deferreds resolves. In this case, .done callbacks will be passed index and value args, where index is the index of the deferred that fired. If non-deferreds are in the arguments to when2, the first one seen will trigger the resolve (not very useful, but consistent).
  • If options.failOnFirstError is false, the deferred returned by when2 will never fail. It will collect the results from all the deferreds and pass them all back. The caller gets to figure out which values (if any) are errors.
  • If no options are given (or options.failOnFirstError is true), fail will be called on the deferred returned by when2 on the first error (this is the behavior of jQuery.when). The args passed to fail will be index and value, indicating which deferred was rejected.
  • Any .progress callbacks added to the returned deferred are also called with index and value arguments so you can tell which deferred made progress.

You can grab the source code, see examples, etc., on Github.

BTW, I’m writing a short (about 75pp) O’Reilly book, Learning jQuery Deferreds, with Nicholas Tollervey. If you’re interested in reviewing early Rough Cuts drafts, please let me know! The book will be out late this year.

Yet another cancelable Twisted deferred class

June 20th, 2013

I’m posting this (completely untested!) to illustrate how one could write a class to provide a Twisted deferred-like class, identical to the twisted.internet.defer.Deferred class, but which lets you call callback, errback, or cancel on the instance yourself. Hopefully that will make some sense. If not, let me know in the comments and I’ll try to be clearer. There’s also discussion of this issue (again) in the Twisted mailing list right now. For context, the discussion thread from January 2010 where I first wrote a class like the below is here.

from twisted.internet.defer import CancelledError, Deferred
from twisted.python.failure import Failure

class ControllableDeferred2013(object):

    """A Deferred-like class that takes a regular Twisted Deferred and
    provides a deferred that can be fired at will. If you have a regular
    Twisted Deferred, you can produce a deferred you have more control over
    by using your Deferred instance to make an instance of this class.

    Any time you need to fire a ControllableDeferred2013 instance for any
    reason, call its callback, errback or cancel method. It will fire
    immediately with the value you provide and the original Deferred will
    be cancelled."""

    def __init__(self, originalDeferred):
        self._fired = False
        self._originalDeferred = originalDeferred
        self._newDeferred = Deferred()
        for method in ('addBoth', 'addCallback', 'addCallbacks', 'addErrback',
                       'chainDeferred'):
            setattr(self, method, getattr(self._newDeferred, method))
        originalDeferred.addBoth(self._originalFired)

    def _originalFired(self, result):
        if not self._fired:
            self._fired = True
            self._originalDeferred.chainDeferred(self._newDeferred)

    def cancel(self, value=None):
        if not self._fired:
            self._fired = True
            self._newDeferred.errback(Failure(CancelledError(value)))
            self._originalDeferred.cancel()

    def callback(self, result=None):
        if not self._fired:
            self._fired = True
            self._newDeferred.callback(result)
            self._originalDeferred.cancel()

    def errback(self, fail=None):
        if not self._fired:
            self._fired = True
            self._newDeferred.errback(fail)
            self._originalDeferred.cancel()

    def pause(self):
        self._newDeferred.pause()
        self._originalDeferred.pause()

    def unpause(self):
        self._newDeferred.unpause()
        self._originalDeferred.unpause()
 

Ten days in hospital

May 23rd, 2013

IMG_5334

I’m about to check out of Addenbrookes Hospital in Cambridge after a 10-day stay, 8 of them in isolation. The short story: I got a rash, and it took over my body. Below are some notes on what’s been going on, along with a few images. You can see the full set of images at http://bit.ly/terry-rash (you may need to register for Dropbox).



April 29 – May 5: The LCHF diet begins

Bacon & eggs!Just back from a week in New York, I decided to start a low carb, high fat (LCHF) diet. I’ve put on about a kilo of weight a year over the last 9 years and eating less carbs seemed like it might be a good way to start burning some of my excess fat. In 2012 I’d done some reading about LCHF diets but hadn’t tried it (check out this video if you’re curious). In NY I’d been feeling too physically large, which prompted me to give it a go. After almost 50 years of living on cereal, bread, pasta, rice and potatoes, though, I wasn’t sure I’d be able to do it.

But it was suprisingly easy. I’d cook myself some bacon and eggs for lunch, along with a big salad, maybe a fried vegetable, nuts and dried beans for snacks, etc. Within a few days I was clearly in ketosis. I wasn’t getting hungry at all because my body had switched to burning my fat, and there was still plenty of that to consume. After 4 days I was down 3kg (from 78 to 75), or about 6.5 pounds. I felt like I was eating more healthily than ever.


Monday May 6: The rash appears

On the morning on Monday May 6, I awoke with a small itchy rash on my sternum, just small red separated dots, probably about 20 of them. I didn’t think too much of it.

Tuesday/Wednesday May 7/8: Initial spread

The rash grew across part of my chest and down my stomach. On the night of May 8th I slept on a fold-out bed downstairs so as not to bug Ana through the night. The rash was extremely itchy, but I knew I must not scratch it. I lay in bed wishing there were manacles by my sides to hold my arms down.

I had began searching around on the web for LCHF rash connections and found there were many hundreds of people blogging and commenting on forums about their rashes. But I couldn’t find anything that looked like reliable scientific opinion on what was happening to those people. Most of them had small rashes and many were able to stop the rash simply by reintroducing carbs. I added some carbs back to my diet, but there was no change. Almost all the comments online were reassuring, saying it was normal, that (unspecified) “toxins” were leaving my cells and exiting through the skin, etc. As so many people had similar problems, and they seemed to just go away after a while, I wasn’t too worried, just very itchy.

Thursday May 9

IMG_20130509_164000

I had to take Findus to a doctor appointment after he got back from school. While there I showed part of my stomach to the doctor. She immediately said it was an allergic reaction. We didn’t discuss it further. I’ve never been allergic to any food, and hadn’t been eating anything new – just different amounts of foods I’d always eaten, and very few carbs. The doctor said she highly doubted the rash could be due to the diet change. She suggested I pick up some calamine lotion, hydrocortisone cream, and anti-allergy tablets to see if any of them helped.

By this time, the fourth day, the rash had spread across under my armpits and slightly down my upper inner arms. It was all across my stomach, and my belly button was all rash (see image below). It had begun to descend onto my upper thighs. It was on my lower legs, just above my ankles, and on the back of my calves. My lower back was covered with it. Did I mention that it was itchy as hell?



Friday May 10

IMG_20130510_114448IMG_20130510_114503IMG_20130510_114608IMG_20130510_114642

I went into Cambridge to meet people I’m doing some part-time research with (on virus detection and discovery, ironically). I was uncomfortable the whole time and spent much of the several hour meeting standing up. Sitting down was causing uncomfortable rubbing pressure on my pants line.

Late that night, Ana convinced me to call the emergency medical line. I didn’t feel it was warranted, but I admit I was worried. Plus, it was Friday night, the local surgery would be closed until Monday, and it was abundantly clear that if this thing didn’t stop spreading it was going to be really bad by then. A woman took my details, listened to the description, and decided to have the medical team call me. They did, and at 11:30pm I was given a midnight appointment at the Chesterton Medical Centre in Cambridge.

The doctor who saw me listened to my story and said I was having an allergic reaction. He put a bunch (8?) of triangular pink steroid tablets into a cup of water and got me to drink it. He wrote me a prescription for more. While talking to him I noticed an unusual slight twinge in my left eye, but didn’t think to say anything.

That night, the rash began to ooze. I noticed when turning over in bed that the sheets seemed to be wet. At first I thought I must be sweating a tiny bit without noticing it. After a while I realized the liquid was coming out of my body in tiny slippery beads of rash pus. Wet areas appeared on the sheets corresponding to my back, stomach, sides, thighs, etc. Very unpleasant. I didn’t sleep well, and not for the last time.



Saturday May 11

20130511_07393620130511_07394520130511_07395320130511_073929

The rash has again spread and become denser. I filled the prescription and went into Cambridge with Sofia to meet Ana and the boys. Back home I took my first steroid dose. My right eye had begun to twinge too. Liquid-filled blisters were beginning to appear on the palms of my hands and on my fingers.



Sunday May 12

After another unpleasant night and more rash spreading, I called the emergency medical people again and got an immediate appointment at the Chesterton Medical Centre. I didn’t take calling for help lightly, but I was constantly uncomfortable and the steroids didn’t seem to have helped at all. The opinion this time was chickenpox. The doctor examined my eye but found no lesions on the cornea. So I went off the steroids and onto aciclovir tablets and aciclovir eye ointment. I was told to go to the Over Surgery on Monday to get a blood test for detection of chickenpox antibodies to confirm the dignosis. I mailed my parents to see if they could remember if I’d had chickenpox already. It seemed very likely that I must have, given that I did not get it when our 3 kids did.

Monday May 13: In isolation at Addenbrookes

IMG_5216IMG_5218IMG_5219IMG_5221

I’m not saying much about the physical side of things. The rash itself was mainly itchy. But my upper body had become extremely sensitive, and I could hardly bear for things to touch it. The sensitivity had been making me tense for some days, so I was not physically relaxed – far from it. My breathing wasn’t natural and even: I’d take in a breath and hold it a little to avoid breathing out and moving my body. Looking in the mirror at my whole body was quite shocking. Given the speed of the spread of rash and the discomfort level, it was clear that in a couple of days things would be really serious.

I called the Over Surgery at 8:30am and got a 9am appointment to have a blood test. I packed a few things in a small bag because I had a feeling I might not be back for a while. As it turned out, I wouldn’t be back home for 10 days.

On arrival, they sent me to a small room where a nurse did the blood samples. She asked why I wanted the test and I told her. I lifted up my top to show her some of my stomach and she said “I’m calling a doctor”. When the doctor did not arrive after a few minutes, she called another. They both soon arrived, and both said the same thing: go straight to hospital.

It’s about a 25 minute drive to Addenbrookes hospital from Over. They tried calling Addenbrookes to speak to the dermatologist, but couldn’t get through. So the doctor wrote me a letter to help with admissions and told me to go to the Accident & Emergency section. I would have much preferred to go in an ambulance than to drive. I guess uncertainty about driving showed in my face or body. The doctor said they could call one, but I declined. I went out and folded myself uncomfortably into the car. I knew I could hold on and keep it together, but I also felt like I was on the edge of some kind of collapse due to sensory or stress overload. It’s hard to explain, but I didn’t feel well at all. I told myself to focus, to act normal, turned on the radio and off I headed.

At Addenbrookes I drove into the carpark. UK parking places are invarably narrow, and I wasn’t looking forward to trying to get into one. Turning my body or stretching to see were painful. Up and up I crawled in a line of cars looking for places, all the way to the 6th level before finding a spot. Then a short walk, feeling a bit geriatric, to A & E.

Inside, they screen all arrivals. I handed over my introduction letter, was given a summary form, and told to sit and wait to be registered. I looked at the summary form, which had the usual mundane information: name, address, etc. At the top, a description of the reason for the visit. Mine said “Rash. Cause?” I was soon called to the registration desk and the woman took more details, including next of kin, and asked if anyone knew I was there. She was telling me I’d need to go sit down again to wait, but I needed attention. I said: “I know ‘rash’ doesn’t sound like much of a problem, but I think I need help right away”. I showed her my stomach. She took one look and sent me back to the woman doing the input screening, and told me to tell her my story. I showed the screening woman my stomach, and that was it. I was out of the admissions area at high speed.

In a small room, they asked me a few questions. I was already in the medical system as “Doctor Jones”. I knew I now had to mention that I’d been doing some work in the Viroscience laboratory at the Erasmus Medical Centre (EMC) in Rotterdam a few weeks earlier. As I wrote to my father later that night in email:

If you ever want to get expedited at top speed through hospital admissions, being called DOCTOR JONES, having spent recent time in one of the world’s top viral labs, and having an unexplained full-on bright scarlet 90%-coverage body rash ain’t a bad combo!

They took me straight to a small isolation room. When they left me there I heard them hang something on the door on the outside – it sounded like they were barring it! Nurses and doctors began to come by to ask questions, take blood samples and nasal/oral swabs. I had a canula line put into the back of my left hand. My heart rate was 120 on admission. Resting it is 60 or less. A Scottish nurse asked to see my back and exclaimed “Oh, you poor, poor, gentleman!”. I had no idea what my back looked like. Bad, I guess. The questions were about recent travel, what I had been doing in Rotterdam, medication, health history. Chickenpox, excema, other illnesses, allergies, the diet change. Sexual practices, drugs, medication, travel, animal exposure, etc.

About 5 hours after arriving, I was admitted and told I wouldn’t be going home that night. After a chest x-ray, I was transferred to an isolation room in Ward L4. It was a ward full of older post-surgery patients, not one used for infectious disease patients. But they needed an isolation room, and that’s what was available. The room was huge, with a window and en suite bathroom. Just as I arrived, I ran into Ana and Derek in the ward looking for me. I was happy to be in hospital, being looked after instead at home looking at my body with mounting dread.

Derek took some photos and emailed them to medical and virology friends at EMC to get their opinions and to ask if anyone else at EMC had come down with a similar condition. The prevailing thinking, at least at Addenbrookes, was that I had chickenpox. The blisters on my palms seemed the strongest indicator. Rather few diseases cause blisters, apparently. My parents had mailed me back to say they couldn’t be sure whether I’d had them.



Tuesday May 14

The night was long and uncomfortable. I wont go into details of the discomfort (see below for some of that). It finally got light around 4:15am. I spent hours standing around in the room naked, sometimes with a blanket around my shoulders but held off my body. I’d lean against the wall to stretch my back, and listen to the clock tick. At some point I realized the rash had gotten into my scalp. I hope my face will continue to remain free from it.

I don’t remember much about the day. Blood samples, swabs, antibiotics going into my arm, more doctors and more questions. Ana and Derek visited again. Ana brought fruit and other supplies, including my Nexus 7. I had almost no comfortable positions that I could maintain for more than a handful of minutes, but lying on my back was fine. The Nexus 7 would become my point of contact with the outside world. The small screen, a case allowing it to stand propped up on my chest, some of my music on it, and connected via a wifi hotspot made by my phone – perfect. I couldn’t have used a laptop.

I met Dr Sterling (head of Dermatology) that day and was very impressed. I also had a swarm of 5 female dermatologists staring intently at my skin as I stood naked in front of them. I told them I wished I had a camera.

An old woman named Margaret has moved into the room next door. She’s confused and disoriented. Every 5 or 10 minutes she walks out of her room and announces “I’m going home”. The nurses tell her she needs to go back to her room, that she’s in hospital, that she can’t go home. “I’m not a child you know, I’m going home.”

Wednesday May 15

IMG_5234IMG_5235IMG_5239IMG_5243

Margaret wanders silently into my room as I’m eating breakfast. I don’t turn around, thinking it is a nurse. Her voice comes from behind me: “wrong house.” I don’t say anything, and she leaves. Later in the day, I am taking a pee and the door to my bathroom is open. I hear someone come in and call out to tell them I’m in the loo. There’s no response. Then I hear a voice call “Margaret, you’re in the wrong room.” Margaret calls back to them “my sister is here, she’s in the toilet.” By the time I come out of the bathroom, Margaret is gone. Later I hear them telling her that she’s going home today: “I certainly am not!” she replies, telling the staff that her friends are coming here to visit her and that she has to leave the ward to find them. No, no, they reassure her, your friends are coming here, they’ll come here to where you are. “But why would they come here?” I feel sorry for them all. Poor Margaret, and the nurses who have to look after her constantly coming out to declare she’s leaving and needing to be shepherded around the ward.

The coalesced parts of rash have gone a dark purple color. Meanwhile the rash has again spread and gotten denser everywhere. Blisters (at least 50 of them) are spreading on my palms and fingers. The VZV (Chickenpox) result came back negative, as did HSV (Herpes Simplex Virus).

With the negative chickenpox result, the case is suddenly more interesting and pressing. I met Dr Moore (head of Infectious Diseases) who comes to see me with Doctor Sterling. Dr Moore stared at me like a hawk, listened to everything very carefully, and then began asking questions. I liked her immediately, she seemed extremely smart and thorough. She suggested they move me to her ward tomorrow so I could be closer to the infectious disease people.

Tomorrow they’ll start throwing every test at me they can think of. Although the rash might be treatable with a steroid cream, the steroids suppress the immune system. So we need to figure out whether my body is busy fighting an actual disease before using steroids to try to settle what could otherwise be some kind of allergic reaction. The doctors here and at EMC start thinking of possibilities while the rest of us are digging for possible leads in Wikipedia, aided by Google Images.

What the hell have I got?



Thursday May 16: Skin pain like on that holiday trip to Mercury

IMG_5249IMG_5259IMG_5268IMG_5270

The canula in my left hand has been hurting a lot when they pump in the antibiotics. So they’ve changed it, and put it into my right forearm. Unfortunately, the plastic dongle attachments hang down to the middle of my forearm where it is unbearably sensitive. Shit.

From a mail I sent to Ana & Derek later this night:

Skin pain like on that holiday trip to Mercury, the one where you forget to take any sunscreen.

During the day, Dr Sterling takes two skin biopsies taken from left forearm. Five stitches in them, in total. One of the samples will go into her -80C freezer and we’ll send it to EMC if necessary.

That night I have a pain in my trunk, back right, as I get up out of a chair. Derek is still there and I tell him. After 15 minutes of waiting for it to go away, we alert the staff. I’m almost unable to raise myself in bed to a sitting position so as to get up. One problem they’re watching out for is any kind of internal infection, so a pain in my right kidney region is a bit of a worry. I’m sent for a chest x-ray in wheelchair, and I have to wear a mask. More blood is taken and sent off for septic analysis.

Ahead of the transfer to Infectious Diseases, I have a shower. I have a 500ml container of anti-bacterial soap-like liquid and I’m supposed to wash my whole body. On my left forearm I have the two bandages (covering the stitches from the biopsies) that are not supposed to get wet, and sticking out the middle of my right forearm are two dangling plastic dongles attached to a canula with a tube going into my arm. So I’ve got one arm that can’t get wet and one that I can’t bend properly, and I’m supposed to have a shower and wash myself?

After somehow managing that, I begin putting liquid paraffin all over the rash. I.e., over my entire body. You know you have a real rash when they provide you with soothing ointments by the liter. Once I’m done, the rash feels better, but I am totally covered in shiny paraffin. I hang out naked for as long as I can and then put on the hospital pyjamas for the ward transfer.

I’m transferred at 8pm to Infectious Disease ward D10, and placed in isolation. There’s an air lock to get into the ward and a “Barrier Nursing” sign on my door. The room is about half the size of the one I’d just left, and cold. Derek goes to ask them if they can turn off the blower, but it’s part of the airflow set-up of the isolation ward. He gets me a small radiator to counter the cold and soon after that, around 9pm, they ask him to leave.

I now have about 9 hours to get through before I’ll see the morning staff. I lay in the dark with the Nexus 7 on my chest and sent a short mail to Russell:

This is utterly hellish. The last ward was a paradise. I half expect Derren to walk in. I should be tweeting it. It’s only 2am. I have to look at it as a survival course. I wish I could write more easily. This is left hand only for various reasons. I should make a list.

I didn’t want to expand on how horrible I felt, because it seemed extra words could only make it seem less horrible than it was. But I had nothing better to do, I was certainly not going to fall asleep, so I might as well write up a list of things that were collectively making this all so unpleasant. So from 2-3:20am, typing with the index finger of my left hand and lying on my back, I got it all down, big and small.

The main problem of course was that my skin was ridiculously sensitive and painful. To ease this, I was covered in paraffin, from toes to neck. The paraffin is all over the inside of the synthetic hospital pyjamas. It soaks, somewhat, into the rasping synthetic sheets of the bed and synthetic pillowcase. The bed blankets are heavy and synthetic, and they too don’t mix well with paraffin. Everything is saturated with paraffin. None of it dries out at all or becomes any less slippery or welcoming. Under the paraffin, concreted to my skin are thousands of small golden crystals of solidified rash pus (see image for May 13). They are very hard and scratchy, and are difficult to dissolve in the shower. It is like having large grains of sand in the paraffin between me and the bed. The air conditioner is constantly on, blowing cool air onto me, and making the sheets cold and dead to the touch.

The skin pain is complimented by lots of things about my arms and hands that make it hard to do anything or be comfortable, apart from lying flat on my back. I have two cuts with stitches in them on my left forearm from the skin biopsies today. One of them has lost its dressing. I have a hospital name tag on my left wrist that is too loose. It will sound trivial, but it was extremely annoying. Hundreds of times, often using my mouth, I move it up and try to wedge it around my wrist, trying to keep it away from my forearm. The back of my left palm is bruised and sore from having a line in it for 3 days. The back of my right palm has been used about 10 times to draw blood. My right elbow crease area has the canula line in it, the tube going up into my vein above my elbow, making it uncomfortable to bend the arm. Two plastic dongle connectors hang from the line right to the exact middle of my ultra-sensitive forearm (see the image below). I need to find ways to position my arm so the plastic connectors are not resting on my forearm. Each time I move the arm, I try to avoid letting them touch me. The line hurts a little when the connectors dangle backwards out into the air, which happen if I raise the arm much (I am lying on my back, so raising the arm is a frequent need). The plastic bandage holding the line to my arm is half off, due to the paraffin, which makes it swing around more than it should.

There are about 60 blisters on my palms, between my fingers, and on the tops/bottoms of my fingers. Some are large, all are pressure sensitive. Closing my hands is awkward / sensitive due to this. Picking things up or, much harder, supporting my weight as I get up hurts the hands, for the same reason. I find it hard to bend down, let alone to reach the ground (skin pain) to pick anything up.

Lying on my back is fine, if I keep my arms angled out and clear of anything that could touch them or my underarms. But I have been lying down so much this past week, my back is very tired. Lying on my side is very uncomfortable due to arms, underarms, dongles, skin etc. I do it from time to time to relieve the back. I find a way to have both arms sticking out from my body, bent at the elbows, nothing touching anything, and I more-or-less hold the position and hope to drift off a little.

One thing I had been saving up and looking forward to as a midnight snack was a bowl of cereal with cold milk. After a couple of hours of lying in the bed, I decided it was time. I got up, feeling cold. I prepared the cereal and went to take the first delicious mouthful. Unbelievably, the milk had gone sour! I had to get warm again and the only place was the bed. But in the few minutes of preparing the cereal, I had left the sheets folded down and the air conditioner had blown on them. They were cold and saturated with paraffin. I forced myself back into the bed, pulled a blanket over myself, and tried to get warm.

At some point, I thought to myself “This is a survival course”. That everything that was going wrong, finishing with finding the milk sour and having to force myself into the wet sheets despite being freezing, was all part of a deliberate plan to break me. All part of a set of challenges I was being thrown and which I had to deal with. I mailed Russell, telling him I expected Derren Brown to walk into the room at any moment with a TV crew. The “survival course” perspective helped, and I began to smile (a little, inwardly). There was no way I was going to let this get the better of me. From then on, things have gotten better.

It was a horrible night. Probably the most uncomfortable of my life.



Friday May 17

IMG_20130517_060747

My feet are now greatly swollen. Getting up from the bed is beginning to be painful. Negative test results come in on HIV and syphilis. During the day additional tests begin to come in.

Yesterday they weighed me: 81.6kg! I’ve somehow added 3.5kg in under 4 days. WTF?

Ana is worried I may have leukemia. It turns out she hasn’t slept for 3 days. The EMC guys have sent some worrying links, e.g., to things like Stevens Johnson disease. She brings me a ton of bedding: pillow cases, sheet, comforter cover, and a bunch of soft warm travel towels. That will make a huge difference.



Saturday May 18

20130518_152205IMG_5290IMG_5308IMG_5302

It’s hard to see that I used to have ankles. My lower legs have turned dark purple with the rash and are very swollen. Large ugly blisters have appeared on inside of my heels, around my ankles, and on my Archilles tendons, on both feet The right leg is worse, but it’s a close thing. The upper part of my body is looking quite a bit better – compare the earlier photo of my underarm.

Dr Sterling drops in. She thinks, given the negative infectious disease results, it’s time to try some steroid cream on just my stomach & chest. She examines my “tree stump” legs and takes a photo with her phone.

Dr Moore drops by too. She looks at my legs and tells me that due to some cellular protein level being low, water that would otherwise be bound and inside my cells is leaving them via osmosis and is basically sloshing around (I am paraphrasing) inside my system, causing the inflammation of my feet. She tells me I have eat well, with as much protein as possible. She suggests protein shakes. I tell Derek, who lives on SPIRU-TEIN, and he brings me a can.

Ana comes by and is still terribly worried I may have leukemia. I call to get a doctor in some that she can hear why they don’t think so. Mok, who works with Dr Moore, comes by to help. He says he’ll ask the lab doing the biopsy to examine the white blood cells (my count is high) for any abnormal shape that might indicate leukemia. Mok comes by later to lance some of the blisters on my heels to collect their liquid for analysis.

That night I have a fever of 39C. But they’d given me paracetamol and codeine which keeps it in check. The codeine makes me feel totally weird. I am aware of the distant sensory roar from my skin as I lie with my arms touching the sheets. I can’t think clearly about anything. I see faces transform into mischievous devil caricatures, with goatees and horns. At about 3am I manage to collect myself and put the steroid cream onto my chest. The codeine weirdness goes on all night, until daylight. In the morning I find the canula in the bed next to me, the tube has been pulled out of my vein. I’ll not be trying the codeine again.



Sunday May 19

IMG_5324IMG_5341IMG_5339IMG_20130519_094017

Sofia and Lucas visit with Ana. We eat Burger King that they bring and watch a movie. They look a bit shocked to see my upper body. I keep my pants on so as not to show my legs, which are much scarier at this point.

Getting into a standing position is now very painful. Pain shoots down my legs as the “sloshing” intra-cellular water pushes down through my legs. Then when my feet touch the ground, some of the water is forced back up my legs and pains shoot up to my inner thigh. Once I manage to stand, I have to take small shuffling painful steps for maybe a minute before things settle down and I can walk almost normally. If this keeps getting worse, getting out of bed is going to be a real problem.

I have very small new blisters on fingers. What’s going on, more blisters?



Monday May 20

IMG_5352IMG_5355IMG_5359IMG_5358

The creep of additional rash redness on feet has continued overnight, and the rash is now between toes. Walking in flip flops affected. I realize I should have been putting the steroid cream onto my entire foot, not just the parts that visually (only) have the rash.

Ana and Derek bring delicious Indian food from Cambridge for lunch.

More infectious disease test results are coming in, all negative.



Tuesday May 21

IMG_20130521_103639

Althea and Edward Parker visit. Rash creep on hands and feet has stopped. Liver structural damage sonogram test is negative. A stool enterovirus test comes back negative, ruling out Hand, Foot and Mouth Disease. I am declared officially non-infective, and get to go downstairs to coffee shop!

Derek drops by at night and we talk virus discovery and a presentation I’m due to give in Rotterdam next Monday. I’ve done no Fluidinfo work or anything else for a couple of weeks.

I am peeling pretty much everywhere. Each morning and evening I wash myself and apply steroid cream. In between I am constantly putting on a skin itchiness/excema moisturizer.



Wednesday May 22

I thought I’d be out today because the key liver test is now showing an improved result. But they want to keep me one more day to do another blood test for liver functionality. I’m tempted to let them take blood in the morning and then check out. If the result is bad, I can come back in.

At Costa coffee downstairs I begin to pull together the images (mine, Derek’s, Ana’s) from the weirdness of the last 9 days. I do some work on putting together the text for this blog post. Costa have a fast and free wifi network which you can use for 3 hours.

My legs are peeling so much. There are many large flakes of skin in the bed each time I get in or out. The peeling skin on my heels and back of my Archilles tendon is very thick. You wouldn’t think that skin could peel, but it can. The skin on your balls (if you have any) can peel too, I can confirm. It feels like I can rub cream into my feet forever and they still could use more. From shoulders down to feet it looks like I have a mild sunburn, skin either peeling or very dry.



Thursday May 23

I’m supposed to have a last blood test today, but they’ve not come as they usually do to take the sample. I’m totally packed up and ready to leave. It’s been 10 days.

Now I’ve had word, I’ll be out soon. They’re preparing the various ointments I’ll need at home. Blood just got taken again and the results will be back in about an hour. Next Wednesday I’ll come back in for another blood test.

Conclusions

There’s no strong conclusion as to what caused my rash. The doctors think the most likely explanation is that it was triggered by a virus, but they don’t know what. It could have been medication, but apart from some cough syrup and lozenges, I wasn’t on any. They say it’s not an allergic reaction, and that it’s not due to the diet change. If they’re right, I’m lucky, as it’s unlikely to re-occur.

The Addenbrookes people have been fantastic. Perhaps 100 people have looked after me over the last ten days. Many doctors, nurses, blood takers, food bringers, cleaners, wheelchair pushers, x-ray and sonograph operators, admin staff. They’re from all over: Poland, Hungary, Latvia, Zimbabwe, South Africa, Australia, Ghana, UK, India, Pakistan, Philipines, China. Everyone has been great. The nurses work 12 hour shifts. In a few hours I’ll be gone from here and someone else will be in this room, being taken care of just as expertly as I was, and the good people working here just keep going and going and going, making each patient feel special and cared for, for thousands of patients a year in this ward alone. I’ve had an unpleasant last 10 days, but compared to what some people go through on a much longer term basis, it has been nothing. I’m lucky, I’ll walk out in almost full health and soon be fully recovered.

And thanks so much to Ana and Derek, for coming by every day and taking so much care of me. That made a huge difference.

Daylight robbery: Barclays skims €170 off a 5K EUR -> GBP transfer

February 5th, 2013

Last month (on Jan 18, 2013) someone I’m doing some work for initiated a transfer of €5,000 into my UK bank account. According to xe.com the mid-market rate that day was 1.1937940679 euros per pound.

So you might innocently expect to receive about 5,000 / 1.1937940679 = £4,188 minus any transfer fees.

The transfer went through an intermediate bank, who charged €17. Barclays charged “our commission” of a mere £6.

But the amount that arrived in my bank was not roughly £4,188 – £20 = £4,168 as you might hope.

The amount that arrived was £4017.74.

The friendly banks decided that the appropriate exchange rate for me that day was 1.23840, which is a full 4.5% higher than the mid-market 1.19379 rate. That’s £143 (€170).

Sure, I know there’s a buy/ask spread in currency and the mid-market rate isn’t what you’d get in any transaction. But taking £143 from your own customer just because you can is pretty fucking nasty. And so, via today’s arbitrary setting of the greed parameter in a bank computer, the voracious banking industry gobbles up just a little bit more of the money made by regular people. People who actually worked to earn that money.

It’s no wonder people hate their banks and that the financial system in general is so despised.

Secure per-site passwords with no encrypted blob

February 3rd, 2013

Last night I read a Guardian article, Online passwords: keep it complicated. It’s a surprisingly good summary, given that it’s aimed at the general public. The author concludes by telling us he decided to adopt LastPass, and also mentions 1Password. The comments section on the page gives similar solutions, like KeePass. The security-conscious people I know have arrived at the same conclusion. There are plenty of articles on the web that summarize various similar products, e.g., Which Password Manager Is The Most Secure? at LifeHacker. A single encrypted blob offers good security and works well in practice. It also allows the storage of information like name, address, credit cards, etc., that can be used to auto-fill web forms.

But… I’ve never liked the idea of a single encrypted file with all my passwords in it. What if the storage is lost or corrupted? Could the file someday be decrypted by someone else? If my encrypted blob is online, what happens when I am offline? If the blob is to be stored locally, I need to think about where to put it, how to back it up, etc. If a company holds it for me, what happens if they go out of business or get hacked? What if they use proprietary encryption, a closed-source access app, or a proprietary underlying data format? Not all the above solutions have all these issues, but they all have some of them.

The crucial thing they all have in common is that they use a master password to encrypt all your passwords into a single blob, and the blob has to then be reliably stored and accessible forever.

An approach that requires no storage

I realized there’s a solution that doesn’t require any storage. It’s not perfect, but it has some attractive properties. I’ve already started using it for some sites.

[Edit: it has been pointed out in the comments that the following solution has been thought of before. See SuperGenPass.]

Here’s a simple first version of Python code to print my password for a given service:

import base64, getpass, hashlib, sys

service = sys.argv[1]
secret = getpass.getpass('Enter your master password: ')
password = base64.b64encode(hashlib.sha512(secret + service).digest())[:32]

print 'Your password on %s is %s' % (service, password)

The service name is given on the command line. The code prints a 32-character password for use on that service. Here’s some sample output:

        $ mypass.py facebook
        Enter your master password: 
        Your password on facebook is Wza2l5Tqy0omgWP+5DDsXjQLO/Mc07N8

        $ mypass.py twitter
        Enter your master password: 
        Your password on twitter is eVhhpjJrmtSa8XnNMu6vLSDhPeO5nFOT

This has some nice advantages. It also places some small requirements on the user. Unfortunately, however, it is not generally applicable – at least not today. These are discussed below.

Advantages

The obvious advantage is that there is no external storage. Your passwords are not stored anywhere. There’s no blob to store, protect, access, backup, worry about, etc. The algorithm used to generate your password is dead-simple, it’s open and available in dozens of languages, and it’s secure.

You’re free to use more than one master password if you like. You can invent your own services names (more on which below).

Requirements / User burden

As with all one-key-to-unlock-them-all approaches, the user obviously needs to remember their master password.

With this approach though, the user also has to remember the name they used for the service they’re trying to access. If you create a password for a service called “gmail” you’ll need to use that exact service name in the future. For me that’s not much of a burden, but I guess it would be for others.

There’s no reason why the list of services you have passwords for couldn’t be stored locally. If the password generator were in a browser extension, it could possibly suggest a service name, like “facebook.com”, based on the domain of the page you were on.

With this approach, it’s even more important that one’s master password be hard to guess. Unlike the single-encrypted-blob approach, anyone who can guess your master password (and the names you use for services) can immediately obtain your passwords. They don’t also need access to the blob – it doesn’t exist.

Additional security can be easily had by, for example, using a convention of adding a constant character to your service names. So, e.g., you could use “facebook*” and “twitter*” as service names, and not tell anyone how you form service names.

General applicability

Unfortunately, there is a major problem with this approach. That’s because different sites have different requirements on passwords. Some of the difficulties can be avoided quite easily, but there’s an additional problem, caused when services change their password policy.

The above code generates a Base64 password string. So, to give some examples, if the service you want a password for doesn’t allow a plus sign in your password, the above code might make something unacceptable to the service. Same thing if they insist that passwords must be at most 12 characters long.

Ironically, these services are insisting on policies that prevent the use of truly secure passwords. They’re usually in place to ensure that short passwords are chosen from a bigger space. It would be better, though more work, to impose restrictions only on short passwords.

In a perfect world, all sites could immediately switch to allowing Base64 passwords of length ≥ 16 (say). Then the above approach would work everywhere and we’d be done.

Varying password length

A general approach to adjusting the generated password is to take some of the Base64 information produced and use it to modify the password. For example, you might not comfortable with all your passwords being the same length, so we can compute a length like this:

import base64, getpass, hashlib, string, sys

b64letters = string.ascii_letters + '0123456789+/'
secret = getpass.getpass('Enter your master password: ')
password = base64.b64encode(hashlib.sha512(secret + service).digest())
lenAdjust = b64letters.find(password[-5]) % 16
print 'Your password on %s is %s' % (service, password[0:16 + lenAdjust])

This generates passwords that are between 16 and 31 characters in length:

        $ ./length-varying.py facebook
        Enter your master password: 
        Your password on facebook is 1nTlVGPhuWZf0l9Sk27
        $ ./length-varying.py twitter
        Enter your master password: 
        Your password on twitter is WE1DVZHAFBx2c3g63tR+Oi3Jxs4xMV

Satisfying site requirements

A possible approach to dealing with per-site password requirements is to have the code look up known services and adjust the initial password it generates to be acceptable. This can easily be done in a secure, random, repeatable way. For example:

  • If a site doesn’t allow upper case, lowercase the password.
  • If a site doesn’t allow digits, replace them with random letters.
  • If a site requires punctuation, you can replace some initial letters in the password with randomly chosen punctuation and then randomly permute the result using the Knuth Shuffle.

Some of these transformations use random numbers. These are easy to obtain: take an unused part of the Base64 string and use it to seed a RNG. For each transformation, you would need to call the RNG a fixed number of times, i.e., independent of the number of random numbers actually used to perform the transformation. That’s necessary in order to keep the RNG in a known state for subsequent transformations (if any).

For example, the following replaces digits 0-9 with a letter from A-J whose case is chosen randomly:

import base64, getpass, hashlib, random, string, sys

def getSeed(chars):
    seed = ord(chars[0])
    for letter in chars[1:]:
        seed = (seed << 8) & ord(letter)
    return seed

service = sys.argv[1]
b64letters = string.ascii_letters + '0123456789+/'
secret = getpass.getpass('Enter your master password: ')
digest = base64.b64encode(hashlib.sha512(secret + service).digest())
lenAdjust = b64letters.find(digest[-5]) % 16

passwordWithDigits = digest[0:16 + lenAdjust]
password = ''

random.seed(getSeed(digest[32:36]))
randoms = [random.randint(0, 1) for _ in passwordWithDigits]

for index, letter in enumerate(passwordWithDigits):
    if letter in '0123456789':
        base = ord('a' if randoms[index] else 'A')
        replacement = chr(base + ord(letter) - ord('0'))
        password += replacement
    else:
        password += letter

print 'Your password on %s is %s' % (service, password)

Here’s the output for the above two services (using the same master password):

        $ ./no-digits.py facebook
        Enter your master password: 
        Your password on facebook is bnTlVGPhuWZfAljSkch
        $ ./no-digits.py twitter
        Enter your master password: 
        Your password on twitter is WEBDVZHAFBxccdgGdtR+OidJxsExMV

As you can see, the digits are replaced with letters (in a biased way, that we can ignore in an informal blog post). The RNG is in a known state because the number of times it has been called is independent of the number of digits in the pre-transformation text.

This approach can be used to transform the initial random sequence into one that satisfies a service’s password restrictions.

It is difficult to reliably associate service names with site policies. To do so might require keeping a file mapping the name a user used for a service to the policy of the site. Although this doesn’t defeat the purpose of this approach (since that file would not need to be stored securely), it is an additional and unwanted pain for the user. Part of the point was to try to entirely avoid additional storage, even if it doesn’t have to be encrypted.

The major problem with per-site requirements

The major problem however is that sites may change their password policy. Even if our program knew the rules for all sites, it would have a real problem if a site changed its policy. The code would need to be updated to generate passwords according to the new site policy. Existing users, supposing they upgraded, would then be shown incorrect passwords and would need to do password resets, which is obviously inconvenient.

Conclusion

I like the above approach a lot, but don’t see a way to solve the issue with changing site policies. I wouldn’t mind building in some rules for known popular sites, but any step in that direction has its problems – at least as far as I can see.

For now, I’m going to start using the above approach on sites that allow a long random password with characters from the Base64 set. That covers the majority of sites I use. Importantly, that includes Google, so if I ever need password resets I can have them sent there, knowing that I can always log in to recover them.

CEL, a Chrome Event Logger

January 27th, 2013

logo-128Last night I wrote CEL, a Chrome Event Logger, a Google Chrome extension that logs all known chrome.* API events to the Javascript console. Example use cases are:

  • You wonder if there is a Chrome API event that’s triggered for some action you take in the browser. Rather than guessing what the event might be and trying to find it in the API docs, you can enable CEL, perform the action in Chrome, and see what CEL logs.
  • You’re writing an extension and are unsure about whether an event is being triggered or with what arguments. Instead of adding an event listener in your own code and reloading your extension, you can just look in the CEL log.

Click here to install.

Viewing the logging

Once installed, you can examine the CEL logging by visiting chrome://extensions, clicking to enable Developer mode, and then clicking the link next to the CEL icon where it says Inspect views: _generated_background_page.html

Manually adjusting logging

In the JS console for the extension’s background page, there are several commands you can run to adjust what is logged:

// Return a list of the chrome.* API events being logged.
CEL.enabled()

// Return a list of the chrome.* API events being ignored.
CEL.disabled()

// Enable logging of some calls (see below).
CEL.enable(name1, name2, …)

// Disable logging of some calls (see below).
CEL.disable(name1, name2, …)
 

The names you pass to CEL.enable and CEL.disable can be individual API calls (without the leading “chrome.”), or can be higher-level categories. Here are some examples:

// Enable chrome.tabs.onCreated and all chrome.webRequest.* events:
CEL.enable(‘tabs.onCreated’, ‘webRequest’)

// Disable all chrome.tabs.* events and chrome.webNavigation.onCommitted
CEL.disable(‘tabs’, ‘webNavigation.onCommitted’)
 

Note that CEL.enable will enable all necessary higher level logging. So, for example, if you call CEL.enable('omnibox.onInputEntered') all chrome.omnibox.* events (that have not been explicitly disabled) will be logged. If you don’t want to enable and disable groups of calls in this way, always pass explicit API calls.

CEL.disabled will show you the names of individual calls that are disabled, as well as any disabled higher levels.

Global enable / disable

The extension provides a context menu item that lets you globally enable or disable logging.

Installation from the Chrome web store

Go to http://bit.ly/chrome-event-logger which will redirect you to the CEL page in the CWS.

Tracking the development version

If you want the development version, you can install the extension by visting https://fluiddb.fluidinfo.com/about/chrome-event-logger/fluidinfo.com/chrome.crx. Chrome will warn you that extensions cannot be installed from non-Chrome Web Store URLs but will download the .crx file in any case. Open chrome://extensions and drag the .crx file you just downloaded onto that page.

Installation from source

The source to the extension is available on Github. Here’s how to clone and install it.

  • Download the repo: git clone http://github.com/terrycojones/chrome-event-logger
  • In Chrome, go to chrome://extensions
  • Click Developer mode
  • Click Load Unpacked Extension…
  • Navigate to the directory where you cloned the repo and click Open

A chrome extension for examining tab events and ids

December 19th, 2012

Yesterday I was on a call with a friend who told me that when he enters a URL into an existing Chrome tab, the tab id changes. He asked if I’d ever seen that happening, and I said no. I told him his code was probably to blame :-)

Anyway, I wrote a quick Chrome extension, called Tabsanity, to log all 8 tab events with the tab ids, as well as to run a simple sanity check on tab ids after every tab event.

All the action is in the Javascript console for the background page.

To see if you’ve got the issue my friend has, open a tab and go to http://en.wikipedia.org/wiki/Virtual_private_network. In the JS console you’ll see the tab id. Now go to the URL location bar, enter nytimes.com, and go to that URL. If Chrome is behaving properly for you, the tab id involved wont change. If you have the issue, the console log will show you that Chrome (quickly) removes the existing tab, creates a new one, and loads the nytimes page – resulting in a different tab id. We were both running Chrome 23.0.1271.101 on a MacBook Air. The same behavior happens in Incognito Mode with all other extensions disabled, and regular mode.

You can install from this link or get the source on Github.

Omit needless parens

December 18th, 2012

The famous 17th commandment in The Elements of Style is “Omit needless words”.

There should be an equivalent in programming, but for parentheses. Every time I see needless parens in a program I want to rip them out (unless they’re obviously there for formatting/readability reasons).

Community service message: Omit needless parens. When in doubt whether parens are needed, look up the precedence rules for the operators involved and only use parens if the default isn’t what you want.

Here’s why you shouldn’t use needless parens:

  • The #1 reason is that you’re making your code more difficult to read for people who know the language better than you do. A more experienced programmer will see a red flag and look at your code more carefully than necessary because they will be trying to figure out why you used the extra parens and if there’s something non-obvious going on. When I come across code like that, I usually conclude that whoever wrote the code doesn’t know the language that well. My opinion of the code goes down. My reading speed goes down too because the needless parens, in my estimation, indicate an increased likelihood that programmer has done other (worse) things elsewhere.
  • You don’t want to appear incompetent or lazy, or to slow down or put off people reading your code, right? (See above.)
  • Putting in needless parens is heading down a slippery slope. How many levels of extra parens should you stop at? The only clear cut rule that makes sense is to stop at zero.
  • If you pause to look up the precedence rules, you’ll make yourself a better programmer in the language in question. You’ll be able to read other people’s needless-parenthesis-free code with no problem. You can pen lofty holier-than-thou blog posts like this one.

Back in about 1985 I wrote a tiny shell script to print out operator precedence and associativity rules for C. When I started programming in Perl, I wrote one for it. Then one for Python and later one for Javascript. For your convenience, and as a reward for reading, I just stuck the 4 scripts up on Github.

Destructive, invasive, and dangerous behavior by UK ISP TalkTalk (aka StalkStalk)

December 5th, 2012

Today I spent several hours trying to figure out what was going wrong with a web service I’ve been building. The service uses websockets to let browsers and the server send messages to each other over a connection that is held open.

I built and tested the service locally and it worked fine. But when I deployed it to a remote server, the websocket connections were mysteriously dropped 30-35 seconds after they were established. The error messages on the server were cryptic, as were those in the browser. Google came to the rescue, and led me to the eye-opening explanation

It turns out TalkTalk, my ISP, are also fetching the URLs my browser fetches, after a delay of 30-35 seconds! I guess they’re not doing it with all URLs, probably because they figure the sites are “safe” and not full of content that might be deemed objectionable. All the accesses come from a single IP address (62.24.252.133), and if you block that address or otherwise deny its connection attempts, the websocket problem goes away immediately.

Dear TalkTalk – this is a very bad idea. Here are 3 strong reasons why:

  1. First of all, you’re breaking the web for your own customers, as seen above. When your customers try to use a new start-up service based on websockets, their experience will be severely degraded, perhaps to the point where the service is unusable. Time and money will be spent trying to figure out what’s going on, and people will not be happy to learn that their ISP is to blame.
  2. Second, there’s a real privacy issue here. I don’t really want to go into it, but I don’t trust my ISP (any ISP) to securely look after data associated with my account, let alone all the web content I look at. I have Google web history disabled. I don’t want my ISP building up a profile of what the people in this house look at online. There’s a big difference between recording the URLs I go to and actually retrieving their content.
  3. Third, it’s downright dangerous. What if I were controlling a medical device via a web interface and TalkTalk were interfering by killing my connections or by replaying my requests? What if there was a security system or some other sensitive controller on the other end? There’s no way on earth TalkTalk should be making requests with unknown effects to an unknown service that they have not been authorized to use. The TalkTalk legal team should consider this an emergency. Something is going to break, perhaps with fatal consequences, and they are going to get sued.

If you want to read more opinions on this issue, try Googling TalkTalk 62.24.252.133. Lots of people have run into this problem and are upset for various reasons.

See also: Phorm.

Max Tabs

December 4th, 2012

Here’s the second of several Chrome and Chromium browser extensions I’ve recently written. Earlier, I posted some of the background motivation in Alternate browsing realities.

After installing Max Tabs, your browser will not allow you to have more than 15 tabs open at once. Any time you try to open more tabs, it will automatically close existing tabs to keep you at the limit. If you later close some tabs and go below the limit, tabs will be reopened to show URLs that were previously automatically closed. I.e., the URLs in tabs that are closed are not forgotten, they’re stored until you’re down to a reasonable number of tabs. (The URLs are not stored across browser sessions, though they easily could be.)

The main idea here is to limit the amount of memory Chrome consumes in keeping tabs open for you. I regularly have about 50 tabs open, sometimes for weeks or even months, on pages I’m planning to read. My laptop gets bogged down as Chrome consumes more and more memory. I’ve long wanted something to limit my tab habit. I didn’t really like any of the options I found in the Chrome Store, so I wrote my own. In case you’re wondering, the extension closes your rightmost open tabs.

Max Tabs installs a context menu item that shows you the number of URLs it has closed. If you click the context menu item, you can disable the extension, at which point it will immediately open tabs for all URLs it automatically closed.

Note that the extension starts out disabled. I set it up that way so it would be less alarming on installation (if you have over 15 tabs open when you install it, it will immediately close as many tabs as needed). Enable it via the context menu.

The extension is not in the Chrome Web Store yet. It’s still very easy to install: just click here to download the extension, then follow these instructions.

If you’re a programmer, or just curious about how to build Chrome extensions, the source code is available on Github. For info on what URLs the extension has closed tabs for, you can look in the console of its background page, accessible from chrome://extensions.

Open It Later

December 4th, 2012

Here’s the first of several Chrome and Chromium browser extensions I’ve recently written. Earlier, I posted some of the background motivation in Alternate browsing realities.

After installing Open It Later, your browser will randomly delay following links you click on. That is, instead of following the link in your existing tab, it immediately closes the tab! :-) If you open a new tab and try to go to a URL, that tab will immediately close too. The URL you were trying to reach will be opened in a new tab at a random future time, between 15 seconds and 5 minutes later.

This is pretty silly, of course. It deliberately goes directly against the idea that there should be an immediate (useful) reaction from your browser when you click a link. Think of it as something that slows you down, that makes your browsing more considered, that gives you a pause during which you might forget about something that you didn’t really need to read anyway.

Open It Later installs a context menu item that shows you the number of URLs that are pending opening. Click the context menu item to disable the extension. Not only will it ungrudgingly disable itself without pause, it will also immediately open all URLs that were scheduled to be opened in the future.

The extension is not in the Chrome Web Store yet. It’s still very easy to install: just click here to download the extension, then follow these instructions.

If you’re a programmer, or just curious about how to build Chrome extensions, the source code is available on Github. For info on when the extension plans to open your URLs, you can look in the console of its background page, accessible from chrome://extensions.

Special thanks to Hugh McLeod, who (unknowingly) provided Open It Later‘s Snake Oil icon:

Image: Hugh McLeod

Alternate browsing realities

December 4th, 2012

I find it interesting to look for things we take for granted, and to ask what the world would look like if the basic assumptions were outlandishly violated.

Recently I’ve been thinking about browsing. What do we all take for granted when browsing? A biggie is that when we click on a link (or open a new tab) the browser should take us to what we asked for, and do it immediately.

Below are some fanciful ways in which that could be different. You can probably think of others. Imagine if:

  • When you click a link, the new page is shown as usual, but only at some random point in the future. Clicking a link or opening a new tab on a URL, causes your current tab to immediately close!
  • Your browser restricts you to having (say) at most 10 tabs open. If you try to open more, the browser automatically picks another open tab and closes it. When you drop back under 10 tabs, a tab that was automatically closed pops into existence again.
  • When you click to follow a link or you open a new tab, the page appears in someone else’s browser, not in yours.
  • You and a group of friends are limited in the number of total tabs you can collectively have open. If you open a tab that takes you over the limit, a random tab is closed in the browser of a random group member. When the group drops under the limit, the tab is re-opened in the browser of a random group member.
  • You and a group of friends are limited so that only one of you can be looking at any given URL. I.e., if you go to a URL that one of your group already has open, their browser automatically closes their tab.
  • When you click on a link, your browser shows you the page and the page also appears in the browsers of a group of friends. If a friend then clicks on a link on that page, your tab follows along.
  • When reading an interesting page, with one click you can send the URL to a group of friends, whose browsers all load the page.

The nice thing about this kind of blue-sky thinking is that it starts out as frivolous or even ridiculous, but can quickly lead to interesting new approaches. For example, the idea of opening tabs in the future comes directly from questioning the immediacy of link following. Hot on the heels of the ridiculous idea of never following links at all, we land right next to the idea of a Read-Later button that millions of people already find very useful.

Anyway….. I decided to have some fun and implement several of the above for the Chrome and Chromium. I’ll write them up separately very soon.

A simple way to calculate the day of the week for any day of a given year

November 11th, 2012

March 29th

Image: Jeremy Church

The other day I read a tweet about how someone was impressed that a friend had been able to tell them the day of the week given an arbitrary date.

There are a bunch of general methods to do this listed on the Wikipedia page for Determination of the day of the week. Typically, there are several steps involved, and you need to memorize some small tables of numbers.

I used to practice that mental calculation (and many others) when I was about 16. Although all the steps are basic arithmetic, it’s not easy to do the calculation in your head in a couple of seconds. Given that most of these questions that you’re likely to face in day-to-day life will be about the current year, it seemed like it might be a poor tradeoff to learn the complicated method to calculate the day of the week for any date if there was a simpler way to do it for a specific year.

The method I came up with after that observation is really simple. It just requires you to memorize a single 12-digit sequence for the current year. The 12 digits correspond to the first Monday for each month.

For example, the sequence for 2012 is 265-274-263-153. Suppose you’ve memorized the sequence and you need to know the day of the week for March 29th. You can trivially calculate that it’s a Thursday. You take the 3rd digit of the sequence (because March is the 3rd month), which is 5. That tells you that the 5th of March was a Monday. Then you just go backward or forward as many weeks and days as you need. The 5th was a Monday, so the 12th, 19th, and 26th were too, which means the 29th was a Thursday.

It’s nice because the amount you need to memorize is small, and you can memorize less digits if you only want to cover a shorter period. The calculation is very simple and always the same in every case, and you never have to think about leap years. At the start of each year you just memorize a single sequence, which is quickly reinforced once you use it a few times.

Here’s Python code to print the sequence for any year.

#!/usr/bin/env python

import datetime, sys

try:
    year = int(sys.argv[1])
except IndexError:
    year = datetime.datetime.today().year

firstDayToFirstMonday = ['1st', '7th', '6th', '5th', '4th', '3rd', '2nd']
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
          'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
summary = ''

for month in range(12):
    firstOfMonth = datetime.datetime(year, month + 1, 1).weekday()
    firstMonday = firstDayToFirstMonday[firstOfMonth]
    print months[month], firstMonday
    summary += firstMonday[0]

print 'Summary:', '-'.join(summary[x:x+3] for x in range(0, 12, 3))

The output for 2012 looks like

Jan 2nd
Feb 6th
Mar 5th
Apr 2nd
May 7th
Jun 4th
Jul 2nd
Aug 6th
Sep 3rd
Oct 1st
Nov 5th
Dec 3rd
Summary: 265-274-263-153

The memory task is made simpler by the fact that there are only 14 different possible sequences. Or, if you consider just the last 10 digits of the sequences (i.e., starting from March), there are only 7 possible sequences. There are only 14 different sequences, so if you use this method in the long term, you’ll find the effort of remembering a sequence will pay off when it re-appears. E.g., 2013 and 2019 both have sequence 744-163-152-742. There are other nice things you can learn that can also make the memorization and switching between years easier (see the Corresponding months section on the above Wikipedia page).

Here are the sequences through 2032:

2012 265-274-263-153
2013 744-163-152-742
2014 633-752-741-631
2015 522-641-637-527
2016 417-426-415-375
2017 266-315-374-264
2018 155-274-263-153
2019 744-163-152-742
2020 632-641-637-527
2021 411-537-526-416
2022 377-426-415-375
2023 266-315-374-264
2024 154-163-152-742
2025 633-752-741-631
2026 522-641-637-527
2027 411-537-526-416
2028 376-315-374-264
2029 155-274-263-153
2030 744-163-152-742
2031 633-752-741-631
2032 521-537-526-416

Simpler Twisted deferred code via decorated callbacks

October 14th, 2012

This morning I was thinking about Twisted deferreds and how people find them difficult to grasp, but how they’re conceptually simple once you get it. I guess most of us tell people a deferred is something to hold a result that hasn’t arrived yet. Sometimes, though, deferreds do have a result in them immediately (e.g., using succeed or fail to get an already-fired deferred).

I wondered if it might work to tell people to think of a deferred as really being the result. If that were literally true, then instead of writing:

d = getPage(...)
d.addErrback(errcheck, args)
d.addCallback(cleanup, args)
d.addCallback(reformat, args)
return d

We might write something like:

result1 = getPage(...)
result2 = errcheck(result1, args)
result3 = cleanup(result2, args)
return reformat(result3, args)

And if you could write that, you could obviously instead write:

return reformat(cleanup(errcheck(getPage(...), args), args), args)

If we could write Twisted code that way, I think using deferreds would be simpler for people unfamiliar with them. We could show them Twisted code and not even have to mention deferreds (see below).

In the style we’re all used to, the programmer manually adds callbacks and errbacks. That’s basically boilerplate. It gets worse when you then need to also use DeferredList, etc. It’s a little confusing to read deferred code at first, because you need to know that the deferred result/failure is automatically passed as the first arg to callbacks/errbacks. It seems to take a year or more for people to finally realize how the callback & errback chains actually interact :-) Also, I wonder how comfortable programmers are with code ordered innermost function first, as in the normal d.addCallback(inner).addCallback(outer) Twisted style, versus outer(inner()), as in the line above.

Anyway… I realized we CAN let people use the succinct style above, by putting boilerplate into decorators. I wrote two decorators, called (surprise!) callback and errback. You can do this:

@errback
def errcheck(failure, arg):
    ...

@callback
def cleanup(page, arg):
    ...

@callback
def reformat(page, arg):
    ...

reformat(cleanup(errcheck(getPage(...), arg1), arg2), arg3)

The deferred callback and errback chains are hooked up automatically. You still get a regular deferred back as the return value.

And… the “deferred” aspect of the code (or at least the need to talk about or explain it) has conveniently vanished.

You can also do things like

func1(getDeferred1(), errcheck(func2(getDeferred2(), getDeferred3())))

This gets the result of deferreds 2 & 3 and (if neither fails) passes the result of calling func2 on both results through to func1, which is called along with the result of deferred 1. You don’t need to use DeferredLists, as the decorator makes them for you. The errcheck function wont be called at all unless there’s an error.

That’s nice compared to the verbose equivalent:

d1 = DeferredList([getDeferred2(), getDeferred3()])
d1.addCallback(func2)
d1.addErrback(errcheck)
d2 = DeferredList([getDeferred1(), d1])
d2.addCallback(func1)

Or the more compact but awkward:

DeferredList([getDeferred(), DeferredList([getDeferred(), getDeferred()]).addCallback(
    func2).addErrback(errcheck)]).addCallback(func1)
 

There’s lots more that could be said about this, but that’s enough for now. The code (surely not bulletproof) and some tests are on Github. I’ll add a README sometime soon. This is still pretty much proof of concept, and some it could be done slightly differently. I’m happy to discuss in more detail if people are interested.

SOBGTR OCCC AILD FUNEX?

August 10th, 2012

Suppose you had to pick a very small set of character strings that you, and only you, could identify without hesitation in a particular way. What would you choose? How small a set could you choose and still be unique? For example, SOBGTR OCCC AILD FUNEX? is a set of strings that I think would uniquely identify me. (My interpretation is below.) I’m pretty sure that almost any subset of 3 of them would suffice. Coming up with a set of two wouldn’t be hard, I don’t think – but it feels risky.

There are 7 billion people on the planet. So if you just pick 3 reasonably obscure acronyms, e.g., things that only 1 person in 2000 would recognize, you’re heading in the right direction (since 2000 cubed is 8 billion). But that’s only if the obscurity of the things you pick is independent. For example, it’s less good to pick 3 computer acronyms from the 1960s than to choose just one of them plus some things from very different areas of your knowledge.

The rules

  1. Each of your strings with its meaning to you must be findable on Google.
  2. To match with you, another person must interpret all your strings the way you do.

Rule 1 prevents you from choosing something like your bank PIN number, that only you could possibly know. Without this rule, everyone could trivially choose a set of one string. The rule makes thinking up a uniquely identifying set for yourself like a game. Given that all your strings and their interpretations are on Google, each of your strings will likely be recognized by someone in the way you recognize it, so your set will probably have at least 2 strings. You need to choose a set of strings whose set of interpretations, taken as a whole, make you unique (Rule 2).

Why is this interesting?

I find this interesting for many reasons. It seems clear that uniquely identifying sets are fairly easy to construct for people and they’re very small. Certainly small enough to fit in a tweet. Although it’s easy to make a set for yourself, it’s hard to make one for someone else – you might even argue that by definition it’s not possible. If someone else makes one, you can’t produce their set of interpretations without spending time on Google, and even then you’d probably have to know the person pretty well.

Is there a new authentication scheme here somewhere? It’s tempting to think yes, but there probably isn’t. This is less secure than asking people for a set of secrets that are not each findable in Google, so anything you come up with is almost certain to be less secure than the same thing based on a set of actual secrets. It’s more of a fun thought exercise (or Twitter game). It’s not hard to imagine some form of authentication. For example, identify which of a set of symbols are special to you (avoiding others chosen randomly from, say, the set of all acronyms), and their correct interpretations for you, and do it rapidly. Or if a clone shows up one day, claiming to be you, and you’ve thoughtfully put a sealed set of unique symbol strings in your safe, you should be able to convince people that you’re the real you :-)

Answer

Here’s my unhesitating interpretation of the set of 4 strings above:

Remember, to be me you have to get them all. It’s not enough to get a couple, or even three of them.