autowisp.tests.test_expressions module

Class Inheritance Diagram

Inheritance diagram of PipelineError, QuantityLookUp, SlotTestCase, TestBareAggregates, TestChecking, TestDependents, TestNeededValues, TestNoProjectNeeded, TestOrdering, TestQuantileNames, TestReachableNames, TestReferencedNames, TestRenamingReferences, TestSlotEvaluation, TestSlotSyntax

Tests for the database-free half of diagnostic expressions.

Everything here runs against a library and values passed in as arguments, so there is no database, no Django and no fixture beyond a dictionary – which is the point of keeping this tier free of both.

class autowisp.tests.test_expressions.SlotTestCase(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.SlotTestCase

What the channel-slot tests share.

The library exercises every shape a slot comes in: two slots; one expression used at two different bindings, and one used twice at the same; a nested expression binding the slots the other way round; a quantity over the time alone, one built on that, and one mixing a subscripted diagnostic with a bare reference.

bg = {'B': array([4. , 3.7]), 'G0': array([3., 1.]), 'R': array([3.9, 2.3])}
evaluate(wanted)[source]

Evaluate wanted against exactly what the walk asked for.

evaluate_one(quantity, channels)[source]

Return the values of a single instantiation.

Sugar for the common case: everything takes and returns one binding set per quantity, which is what lets two axes be one quantity in two channels, and most cases here want neither.

fetch(wanted)[source]

Return exactly the values needed() asks for.

Deliberately not “every channel available”: supplying more would hide a walk that under-reports, and that walk answers the series table as well as the fetch, so an omission there is a wrong count rather than merely a missing array.

jd = array([1., 3.])

Two images’ worth, distinct per channel so that reading the wrong one cannot pass by coincidence.

library = {'inner': 'bg_center[1] / bg_center[2]', 'mixed': 'bg_center[1] * night', 'night': 'jd - nanmin(jd)', 'outer': 'inner[2,1] + bg_center[1]', 'scaled_night': 'night * 2', 'silly': 'sky_color[1,2] - sky_color[2,3]', 'sky_color': 'bg_center[1] / bg_center[2]', 'twice': 'sky_color[1,2] + sky_color[1,2]'}
needed(wanted)[source]

Return what has to be read for wanted.

class autowisp.tests.test_expressions.TestBareAggregates(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestBareAggregates

Spotting the median-where-nanmedian-was-meant mistake.

test_flags_bare_and_ignores_nan_forms()[source]

Both halves matter: a false positive would nag on good input.

test_ignores_names_that_are_not_calls()[source]

A diagnostic called sum is not an aggregate call.

class autowisp.tests.test_expressions.TestChecking(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestChecking

What is reported as wrong with a proposed expression.

check(name, expression, expressions=None)[source]

Return the problems, using the shared library by default.

test_a_good_expression_has_no_problems()[source]

The case that must not produce noise.

test_cycle_with_an_existing_expression()[source]

Editing one end of a pair is how a cycle usually arrives.

test_name_may_not_shadow_a_diagnostic()[source]

Both are variables in one flat space, so it is ambiguous.

test_name_must_be_a_slug()[source]

Anything else could be stored but never put in a URL.

test_problems_accumulate()[source]

A bad name and a bad body are both worth saying at once.

test_self_reference_is_a_cycle()[source]

Caught by ordering the candidate library, not a special case.

test_statements_are_reported_not_raised()[source]

The security guard, surfaced as a problem for the user.

test_unknown_name_is_reported()[source]

Naming what could not be resolved.

class autowisp.tests.test_expressions.TestDependents(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestDependents

The delete guard.

test_finds_every_dependent()[source]

Both members of the diamond, not just the first.

test_unreferenced_expression_has_none()[source]

Deleting this one would break nothing.

class autowisp.tests.test_expressions.TestNeededValues(methodName='runTest')[source]

Bases: SlotTestCase

Inheritance diagram of autowisp.tests.test_expressions.TestNeededValues

What has to be read before anything is evaluated.

Answered for two callers at once – whatever reads a series in one query, and the table counting the images that record all of it without evaluating anything – so it has to be exact in both directions. The assertions compare whole dictionaries for that reason: what is not needed matters as much, since over-reporting makes a count wrong rather than merely fetching too much.

test_a_binding_of_the_wrong_length_is_refused()[source]

Too few or too many: either would plot something else.

Too many is the one that could pass unnoticed – the extra channel binds no parameter, so it would simply be dropped.

test_one_expression_at_two_bindings_needs_both()[source]

The channels come from resolving each reference’s arguments.

test_one_quantity_asked_for_at_two_bindings()[source]

Both are walked, neither replacing the other.

The two axes of a plot may be one quantity read in two channels – which is how a diagnostic is compared between them – so what is wanted cannot be one binding per quantity.

test_slots_bound_the_other_way_round_are_followed()[source]

inner[2,1] reads the outer binding reversed.

test_the_time_is_found_through_a_bare_reference()[source]

mixed reads the time only by way of night.

The time is the one quantity written without a subscript, so the walk over subscripts never reaches it however deep it lies.

class autowisp.tests.test_expressions.TestNoProjectNeeded(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestNoProjectNeeded

Validation is the same everywhere, so it needs no database.

A diagnostic_type row can only be seeded from the static catalogue or created by the quantile branch, which refuses every other name. So the vocabulary cannot vary between projects, and an expression means the same thing in all of them – which is what lets one library be shared.

test_a_catalogue_diagnostic_resolves()[source]

Even though nothing here has opened a project database.

test_a_misspelling_does_not()[source]

The check is still a check: unknown names are still refused.

test_jd_is_a_variable()[source]

Not a diagnostic, but a name in the same flat space.

class autowisp.tests.test_expressions.TestOrdering(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestOrdering

What has to be evaluated, and in what order.

test_chain_is_ordered()[source]

A reference comes before the expression using it.

test_cycle_names_every_expression_involved()[source]

The whole loop, in the order it closes.

test_cycle_names_the_loop_and_not_its_dependents()[source]

An expression merely downstream of a cycle is not implicated.

d is unreachable because a and b reference each other, but d is not itself part of the problem and naming it would send the reader to the wrong expression.

test_diamond_places_the_shared_expression_once()[source]

The property that makes evaluation non-redundant.

test_needed_diagnostics_are_transitive()[source]

Reported through the chain, not just one level down.

test_only_the_targets_subtree_is_ordered()[source]

Asking for one expression does not drag in the library.

test_plain_diagnostic_needs_no_evaluation()[source]

A target that is simply recorded orders nothing.

test_unknown_target_is_refused()[source]

A bookmarked URL naming nothing should not evaluate.

test_unresolvable_reference_is_refused()[source]

And says which expression contains it.

class autowisp.tests.test_expressions.TestQuantileNames(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestQuantileNames

The diagnostics named by a pattern rather than listed.

pixel_q* diagnostics are created by calibrate rather than seeded, so they are the one part of the vocabulary that cannot be enumerated. They must still resolve as variables and still be refused as expression names, and both come from the same predicate.

test_a_near_miss_is_still_unresolvable()[source]

The pattern must not become a licence for anything similar.

test_a_quantile_may_not_be_taken_as_a_name()[source]

The predicate reserves as well as resolves, which is easy to miss.

Otherwise an expression could be named pixel_q999 and shadow a real quantile – ambiguous in a flat name space, and undetectable afterwards because by then both are simply variables.

test_a_quantile_resolves_as_a_variable()[source]

Nothing enumerates these, so only the pattern can accept them.

test_a_quantile_survives_the_ordering_pass()[source]

A composed expression must not be rejected by the cycle check.

Ordering and the direct check have to agree about what a name may mean, or check_expression contradicts itself: accepting a name, then reporting it unresolvable from the pass looking for cycles. One shared predicate is what makes that impossible.

test_the_family_name_does_not_resolve_as_a_variable()[source]

Reserved is not the same as readable, and here they differ.

A family has no values of its own, so an expression referencing it is a mistake rather than a way of reaching every quantile at once.

test_the_family_name_may_not_be_taken_either()[source]

pixel_quantiles is a selector name, so it is reserved too.

It is the one reserved name that is not a readable quantity: it stands for the whole pixel_q* family, expanding to one series per member. An expression allowed to take it would be swallowed by that expansion and silently never drawn.

class autowisp.tests.test_expressions.TestReachableNames(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestReachableNames

What an expression is allowed to reach.

Parsing is not what makes this safe – most attacks are perfectly valid expressions. The evaluator’s symbol table is, and these pin the part of it AutoWISP chooses rather than inherits.

test_filesystem_access_is_refused()[source]

open is dropped from the evaluator, so the name is unknown.

Asteval permits reading files; harmless for text the local user typed, but expressions travel between installations in export files, so a shared one must not be able to read ~/.ssh.

test_hidden_filesystem_access_is_refused()[source]

Also when buried in something that would otherwise plot.

test_side_effects_are_refused()[source]

print returns nothing, so it was never a valid value.

test_the_mathematical_subset_still_works()[source]

The removals must not cost anything anyone would write.

class autowisp.tests.test_expressions.TestReferencedNames(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestReferencedNames

Reading the names out of an expression.

test_names_include_functions()[source]

Splitting them apart is the caller’s job, not this one’s.

test_statements_are_rejected()[source]

mode="eval" refuses anything that is not one expression.

class autowisp.tests.test_expressions.TestRenamingReferences(methodName='runTest')[source]

Bases: TestCase

Inheritance diagram of autowisp.tests.test_expressions.TestRenamingReferences

Carrying dependents through a rename.

The one place expression text is rewritten, so what it must not touch matters as much as what it must.

test_a_longer_name_containing_it_is_untouched()[source]

The case that kills a textual replace.

rel_bg merely starts with rel; renaming rel must leave it alone, and only ast knows where one identifier ends.

test_a_string_literal_is_untouched()[source]

Text that merely looks like the name is not a reference.

test_an_unmentioned_name_changes_nothing()[source]

Every expression in a library is offered to this, not just the dependents.

test_every_use_is_renamed()[source]

Including two uses in the one expression.

test_offsets_survive_a_non_ascii_character()[source]

ast counts utf-8 bytes, so the splice has to as well.

A multi-byte character earlier in the line shifts every offset after it, and getting this wrong corrupts the text rather than failing.

test_spacing_and_parentheses_survive()[source]

Splicing rather than unparsing, so nothing is reformatted.

class autowisp.tests.test_expressions.TestSlotEvaluation(methodName='runTest')[source]

Bases: SlotTestCase

Inheritance diagram of autowisp.tests.test_expressions.TestSlotEvaluation

Evaluating quantities against the values the walk asked for.

Every case here fetches exactly what the walk reported, so each also asserts the two agree – the pairing that would otherwise drift apart unnoticed.

test_a_chain_of_channel_free_expressions()[source]

scaled_night reads night reads the time, all bare.

Each has to be a value in the symbol table rather than a lookup, since a bare name never reaches __getitem__, and in an order that respects what they read.

test_a_nested_binding_is_not_the_callers()[source]

outer[1,2] = inner[2,1] + bg_center[1].

The case that returns a wrong number rather than an error if a lookup ever holds a binding of its own instead of reading the one belonging to the body being evaluated.

test_a_plain_diagnostic_is_a_quantity_too()[source]

One channel, no expression, and the same call resolves it.

test_a_quantity_over_the_time_alone()[source]

Written bare, there being no channel to subscript it with.

test_an_instantiation_is_computed_once()[source]

Asked for twice, the same array comes back.

Identity rather than equality, and rather than the size of the cache: re-evaluating would overwrite the one entry with an equal but distinct array, so only identity tells the two apart.

test_an_unfetched_channel_is_reported()[source]

A miss means the walk and the fetch disagree, which is a fault in the pair rather than something to repair here.

test_both_axes_at_once_share_their_instantiations()[source]

Which is why the two are resolved in one call, not one each.

test_one_body_evaluated_at_two_bindings()[source]

sky_color is B/R in one term and R/G0 in the other.

test_one_diagnostic_drawn_in_two_channels()[source]

bg_center against bg_center, which is a colour plot.

Both come back, told apart by the binding that asked for each. A result keyed by quantity alone would hold one of them and draw it on both axes, which looks like a perfectly good plot.

test_only_what_is_reached_is_built()[source]

The library holds expressions about data nobody fetched.

Building those would waste work, and for one read bare – which is evaluated on sight, so that it can be a value – would raise about values nobody asked for.

test_slotted_and_channel_free_in_one_expression()[source]

mixed subscripts one quantity and reads another bare.

class autowisp.tests.test_expressions.TestSlotSyntax(methodName='runTest')[source]

Bases: SlotTestCase

Inheritance diagram of autowisp.tests.test_expressions.TestSlotSyntax

Reading channel slots out of an expression.

test_a_slot_must_be_a_whole_number()[source]

Anything else cannot name a channel, and says so here rather than failing later as an unresolvable name.

test_arity_is_a_rule_not_a_table()[source]

Two of the three answers are constants.

test_one_slot_is_still_a_tuple()[source]

So no caller has to care how many were written.

test_parameters_are_derived_and_ordered()[source]

Nothing is declared, so nothing can drift out of step.

test_parameters_are_sorted_whatever_order_the_body_uses()[source]

The order is what a reference’s arguments are matched onto.

test_references_keep_their_slots_and_repeats()[source]

One name at two bindings is the point of the parameters.

autowisp.tests.test_expressions._library = {'offset': 'rel[1] + bg_center[1]', 'rel': 'astrom_residual[1] / diagonal_fov[1]', 'scaled': 'rel[1] / nanmedian(rel[1])'}

two expressions share rel. Everything is read in slot 1, this being about composition rather than about channels; the slot classes below bind several.

Type:

A small library with a diamond in it