Skip to content

Commit

Permalink
Tighter enforcement of contracts.
Browse files Browse the repository at this point in the history
  • Loading branch information
orenbenkiki committed Jun 19, 2024
1 parent b1e7c89 commit c243806
Show file tree
Hide file tree
Showing 11 changed files with 1,949 additions and 337 deletions.
2 changes: 1 addition & 1 deletion docs/v0.1.0/.documenter-siteinfo.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-06-15T21:56:23","documenter_version":"1.4.1"}}
{"documenter":{"julia_version":"1.10.4","generation_timestamp":"2024-06-19T12:37:39","documenter_version":"1.4.1"}}
133 changes: 127 additions & 6 deletions docs/v0.1.0/contracts.html
Original file line number Diff line number Diff line change
Expand Up @@ -426,6 +426,115 @@ <h1 id="Contracts">
<header>
<a class="docstring-article-toggle-button fa-solid fa-chevron-down" href="javascript:;" title="Collapse docstring">
</a>
<a class="docstring-binding" id="Daf.Contracts.contractor" href="#Daf.Contracts.contractor">
<code>Daf.Contracts.contractor
</code>
</a>
<span class="docstring-category">Function
</span>
</header>
<section>
<div>
<pre>
<code class="language-julia hljs">function contractor(
computation::AbstractString,
contract::Contract,
daf::DafReader;
overwrite::Bool,
)::ContractDaf
</code>
</pre>
<p>Wrap a
<code>daf
</code> data set to enforce a
<code>contract
</code> for some
<code>computation
</code>, possibly allowing for
<code>overwrite
</code> of existing outputs.
</p>
<div class="admonition is-info">
<header class="admonition-header">Note
</header>
<div class="admonition-body">
<p>If the
<code>contract
</code> specifies any outputs, the
<code>daf
</code> needs to be a
<code>DafWriter
</code>.
</p>
</div>
</div>
</div>
</section>
</article>
<article class="docstring">
<header>
<a class="docstring-article-toggle-button fa-solid fa-chevron-down" href="javascript:;" title="Collapse docstring">
</a>
<a class="docstring-binding" id="Daf.Contracts.ContractDaf" href="#Daf.Contracts.ContractDaf">
<code>Daf.Contracts.ContractDaf
</code>
</a>
<span class="docstring-category">Type
</span>
</header>
<section>
<div>
<pre>
<code class="language-julia hljs">struct ContractDaf &lt;: DafWriter ... end
</code>
</pre>
<p>A
<a href="formats.html#Daf.Formats.DafWriter">
<code>DafWriter
</code>
</a> wrapper which restricts access only to the properties listed in some
<a href="contracts.html#Daf.Contracts.Contract">
<code>Contract
</code>
</a>. This also tracks which properties are accessed, so when a computation is done, we can verify that all required inputs were actually accessed. If they weren&#39;t, then they weren&#39;t really required (should have been marked as optional instead).
</p>
<p>This isn&#39;t exported and isn&#39;t created manually; instead call
<a href="contracts.html#Daf.Contracts.contractor">
<code>contractor
</code>
</a>, or, better yet, use the
<code>@computation
</code> macro.
</p>
<div class="admonition is-info">
<header class="admonition-header">Note
</header>
<div class="admonition-body">
<p>If the
<a href="contracts.html#Daf.Contracts.Contract">
<code>Contract
</code>
</a> specifies no outputs, then this becomes effectively a read-only
<code>Daf
</code> data set; however, to avoid code duplication, it is still a
<a href="formats.html#Daf.Formats.DafWriter">
<code>DafWriter
</code>
</a> rather than a
<a href="formats.html#Daf.Formats.DafReader">
<code>DafReader
</code>
</a>.
</p>
</div>
</div>
</div>
</section>
</article>
<article class="docstring">
<header>
<a class="docstring-article-toggle-button fa-solid fa-chevron-down" href="javascript:;" title="Collapse docstring">
</a>
<a class="docstring-binding" id="Daf.Contracts.verify_input" href="#Daf.Contracts.verify_input">
<code>Daf.Contracts.verify_input
</code>
Expand All @@ -436,12 +545,12 @@ <h1 id="Contracts">
<section>
<div>
<pre>
<code class="language-julia hljs">verify_input(daf::DafReader, contract::Contract, computation::AbstractString)::Nothing
<code class="language-julia hljs">verify_input(contract_daf::ContractDaf)::Nothing
</code>
</pre>
<p>Verify the
<code>daf
</code> data when a computation is invoked. This verifies that all the required data exists and is of the appropriate type, and that if any of the optional data exists, it has the appropriate type.
<code>contract_daf
</code> data before a computation is invoked. This verifies that all the required data exists and is of the appropriate type, and that if any of the optional data exists, it has the appropriate type.
</p>
</div>
</section>
Expand All @@ -460,12 +569,12 @@ <h1 id="Contracts">
<section>
<div>
<pre>
<code class="language-julia hljs">verify_output(daf::DafReader, contract::Contract, computation::AbstractString)::Nothing
<code class="language-julia hljs">verify_output(contract_daf::ContractDaf)::Nothing
</code>
</pre>
<p>Verify the
<code>daf
</code> data when a computation is complete. This verifies that all the guaranteed output data exists and is of the appropriate type, and that if any of the optional output data exists, it has the appropriate type.
<code>contract_daf
</code> data when a computation is complete. This verifies that all the guaranteed output data exists and is of the appropriate type, and that if any of the optional output data exists, it has the appropriate type. It also verifies that all the required inputs were accessed by the computation.
</p>
</div>
</section>
Expand Down Expand Up @@ -498,6 +607,12 @@ <h2 id="Index">
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.ContractDaf">
<code>Daf.Contracts.ContractDaf
</code>
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.ContractData">
<code>Daf.Contracts.ContractData
</code>
Expand All @@ -510,6 +625,12 @@ <h2 id="Index">
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.contractor">
<code>Daf.Contracts.contractor
</code>
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.verify_input">
<code>Daf.Contracts.verify_input
</code>
Expand Down
12 changes: 12 additions & 0 deletions docs/v0.1.0/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -848,6 +848,12 @@ <h1 id="Index">
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.ContractDaf">
<code>Daf.Contracts.ContractDaf
</code>
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.ContractData">
<code>Daf.Contracts.ContractData
</code>
Expand Down Expand Up @@ -1382,6 +1388,12 @@ <h1 id="Index">
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.contractor">
<code>Daf.Contracts.contractor
</code>
</a>
</li>
<li>
<a href="contracts.html#Daf.Contracts.verify_input">
<code>Daf.Contracts.verify_input
</code>
Expand Down
Binary file modified docs/v0.1.0/objects.inv
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/v0.1.0/search_index.js

Large diffs are not rendered by default.

42 changes: 31 additions & 11 deletions src/computations.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export DEFAULT
using ..Contracts
using ..Formats
using ..Messages
using ..StorageTypes
using DocStringExtensions
using ExprTools

Expand All @@ -26,20 +27,39 @@ function computation_wrapper(::AbstractString, inner_function)
end

function computation_wrapper(contract::Contract, name::AbstractString, inner_function)
return (daf::DafReader, args...; kwargs...) -> (verify_input(contract, name, daf);
result = inner_function(daf, args...; kwargs...);
verify_output(contract, name, daf);
result) # flaky tested
return (daf::DafReader, args...; kwargs...) -> (
#! format: off
contract_daf = contractor(name, contract, daf; overwrite = kwargs_overwrite(kwargs));
verify_input(contract_daf);
result = inner_function(contract_daf, args...; kwargs...);
verify_output(contract_daf);
result # flaky tested
#! format: on
)
end

function computation_wrapper(first_contract::Contract, second_contract::Contract, name::AbstractString, inner_function)
return (first_daf::DafReader, second_daf::DafReader, args...; kwargs...) ->
(verify_input(first_contract, name, first_daf);
verify_input(second_contract, name, second_daf);
result = inner_function(first_daf, second_daf, args...; kwargs...);
verify_output(first_contract, name, first_daf);
verify_output(second_contract, name, second_daf);
result) # flaky tested
return (first_daf::DafReader, second_daf::DafReader, args...; kwargs...) -> ( # NOJET
#! format: off
first_contract_daf = contractor(name * ".1", first_contract, first_daf; overwrite = kwargs_overwrite(kwargs));
second_contract_daf = contractor(name * ".2", second_contract, second_daf; overwrite = kwargs_overwrite(kwargs)); # NOJET
verify_input(first_contract_daf);
verify_input(second_contract_daf);
result = inner_function(first_contract_daf, second_contract_daf, args...; kwargs...);
verify_output(first_contract_daf);
verify_output(second_contract_daf);
result # flaky tested
#! format: on
)
end

function kwargs_overwrite(kwargs::Base.Pairs)::Bool
for (name, value) in kwargs
if name == :overwrite
return value
end
end
return false
end

struct FunctionMetadata
Expand Down
Loading

0 comments on commit c243806

Please sign in to comment.