Skip to content

Commit 42c2c11

Browse files
committed
API handling tools
1 parent 1d0ea9f commit 42c2c11

File tree

8 files changed

+341
-0
lines changed

8 files changed

+341
-0
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.jl.cov
2+
*.jl.*.cov
3+
*.jl.mem
4+
.DS_Store

.travis.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
## Documentation: http://docs.travis-ci.com/user/languages/julia/
2+
language: julia
3+
os:
4+
- linux
5+
- osx
6+
julia:
7+
- 0.6
8+
- nightly
9+
notifications:
10+
email: false
11+
git:
12+
depth: 99999999
13+
14+
## uncomment the following lines to allow failures on nightly julia
15+
## (tests will run but not make your overall status red)
16+
#matrix:
17+
# allow_failures:
18+
# - julia: nightly
19+
20+
## uncomment and modify the following lines to manually install system packages
21+
#addons:
22+
# apt: # apt-get for linux
23+
# packages:
24+
# - gfortran
25+
#before_script: # homebrew for mac
26+
# - if [ $TRAVIS_OS_NAME = osx ]; then brew install gcc; fi
27+
28+
## uncomment the following lines to override the default test script
29+
script:
30+
- julia -e 'Pkg.clone(pwd()); Pkg.test("APITools"; coverage=true)'
31+
after_success:
32+
# push coverage results to Coveralls
33+
- julia -e 'cd(Pkg.dir("APITools")); Pkg.add("Coverage"); using Coverage; Coveralls.submit(Coveralls.process_folder())'
34+
# push coverage results to Codecov
35+
- julia -e 'cd(Pkg.dir("APITools")); Pkg.add("Coverage"); using Coverage; Codecov.submit(Codecov.process_folder())'

LICENSE.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The APITools.jl package is licensed under the MIT "Expat" License:
2+
3+
Copyright (c) 2018: Gandalf Software, Inc. (Scott Paul Jones)
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
22+

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# APITools
2+
3+
[![Build Status](https://travis-ci.org/JuliaString/APITools.jl.svg?branch=master)](https://travis-ci.org/JuliaString/APITools.jl)
4+
5+
[![Coverage Status](https://coveralls.io/repos/github/JuliaString/APITools.jl/badge.svg?branch=master)](https://coveralls.io/github/JuliaString/APITools.jl?branch=master)
6+
7+
[![codecov.io](http://codecov.io/github/JuliaString/APITools.jl/coverage.svg?branch=master)](http://codecov.io/github/JuliaString/APITools.jl?branch=master)
8+
9+
The `APITools` package is now working on both the release version (v0.6.2) and the latest master (v0.7.0-DEV).

appveyor.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
environment:
2+
matrix:
3+
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x86/0.6/julia-0.6-latest-win32.exe"
4+
- JULIA_URL: "https://julialang-s3.julialang.org/bin/winnt/x64/0.6/julia-0.6-latest-win64.exe"
5+
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
6+
- JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
7+
8+
## uncomment the following lines to allow failures on nightly julia
9+
## (tests will run but not make your overall status red)
10+
#matrix:
11+
# allow_failures:
12+
# - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x86/julia-latest-win32.exe"
13+
# - JULIA_URL: "https://julialangnightlies-s3.julialang.org/bin/winnt/x64/julia-latest-win64.exe"
14+
15+
branches:
16+
only:
17+
- master
18+
- /release-.*/
19+
20+
notifications:
21+
- provider: Email
22+
on_build_success: false
23+
on_build_failure: false
24+
on_build_status_changed: false
25+
26+
install:
27+
- ps: "[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12"
28+
# If there's a newer build queued for the same PR, cancel this one
29+
- ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
30+
https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
31+
Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
32+
throw "There are newer queued builds for this pull request, failing early." }
33+
# Download most recent Julia Windows binary
34+
- ps: (new-object net.webclient).DownloadFile(
35+
$env:JULIA_URL,
36+
"C:\projects\julia-binary.exe")
37+
# Run installer silently, output to C:\projects\julia
38+
- C:\projects\julia-binary.exe /S /D=C:\projects\julia
39+
40+
build_script:
41+
# Need to convert from shallow to complete for Pkg.clone to work
42+
- IF EXIST .git\shallow (git fetch --unshallow)
43+
- C:\projects\julia\bin\julia -e "versioninfo();
44+
Pkg.clone(pwd(), \"APITools\"); Pkg.build(\"APITools\")"
45+
46+
test_script:
47+
- C:\projects\julia\bin\julia -e "Pkg.test(\"APITools\")"

src/APITools.jl

Lines changed: 175 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,175 @@
1+
__precompile__(true)
2+
"""
3+
API Tools package
4+
5+
Copyright 2018 Gandalf Software, Inc., Scott P. Jones
6+
Licensed under MIT License, see LICENSE.md
7+
8+
(@def macro "stolen" from DiffEqBase.jl/src/util.jl :-) )
9+
"""
10+
module APITools
11+
export @api, @def
12+
13+
macro def(name, definition)
14+
quote
15+
macro $(esc(name))()
16+
esc($(Expr(:quote, definition)))
17+
end
18+
end
19+
end
20+
21+
struct TMP_API
22+
base::Vector{Symbol}
23+
public::Vector{Symbol}
24+
develop::Vector{Symbol}
25+
define_public::Vector{Symbol}
26+
define_develop::Vector{Symbol}
27+
TMP_API() = new(Symbol[], Symbol[], Symbol[], Symbol[], Symbol[])
28+
end
29+
30+
const SymList = Tuple{Vararg{Symbol}}
31+
32+
struct API
33+
mod::Module
34+
base::SymList
35+
public::SymList
36+
develop::SymList
37+
define_public::SymList
38+
define_develop::SymList
39+
40+
API(mod, api::TMP_API) =
41+
new(mod,
42+
SymList(api.base), SymList(api.public), SymList(api.develop),
43+
SymList(api.define_public), SymList(api.define_develop))
44+
end
45+
46+
const APIList = Tuple{Vararg{API}}
47+
48+
"""
49+
@api <cmd> [<symbols>...]
50+
51+
* @api init # set up module/package for adding names
52+
* @api freeze # use at end of module, to "freeze" API
53+
54+
* @api use <modules>... # use for normal use
55+
* @api test <modules>... # using api and dev, for testing purposes
56+
* @api extend <modules>... # for development, imports api & dev, use api & dev definitions
57+
* @api export <modules>... # export api symbols
58+
59+
* @api base <names...> # Add functions from Base that are part of the API
60+
* @api public <names...> # Add functions that are part of the public API
61+
* @api develop <names...> # Add functions that are part of the development API
62+
* @api define_public <names...> # Add other symbols that are part of the public API (structs, consts)
63+
* @api define_develop <names...> # Add other symbols that are part of the development API
64+
"""
65+
macro api(cmd::Symbol)
66+
if cmd == :init
67+
quote
68+
global __tmp_api__ = APITools.TMP_API()
69+
global __tmp_chain__ = Vector{APITools.API}[]
70+
end
71+
elseif cmd == :freeze
72+
@static if VERSION < v"0.7.0-DEV"
73+
esc(quote
74+
const __chain__ = APITools.APIList(__tmp_chain__)
75+
const __api__ = APITools.API(current_module(), __tmp_api__)
76+
__tmp_chain__ = _tmp_api__ = nothing
77+
end)
78+
else
79+
esc(quote
80+
const __chain__ = APITools.APIList(__tmp_chain__)
81+
const __api__ = APITools.API(@__MODULE__, __tmp_api__)
82+
__tmp_chain__ = _tmp_api__ = nothing
83+
end)
84+
end
85+
else
86+
error("@api unrecognized command: $cmd")
87+
end
88+
end
89+
90+
const _cmdadd = (:define_public, :define_develop, :public, :develop, :base)
91+
const _cmduse = (:use, :test, :extend, :export)
92+
93+
@static VERSION < v"0.7.0-DEV" && (const _ff = findfirst)
94+
@static VERSION < v"0.7.0-DEV" ||
95+
(_ff(lst, val) = coalesce(findfirst(isequal(val), lst), 0))
96+
97+
function _add_symbols(grp, exprs)
98+
symbols = Symbol[]
99+
for ex in exprs
100+
if isa(ex, Expr) && ex.head == :tuple
101+
append!(symbols, ex.args)
102+
elseif isa(ex, Symbol)
103+
push!(symbols, ex)
104+
else
105+
error("@api $grp: syntax error $ex")
106+
end
107+
end
108+
esc(:( append!(__tmp_api__.$grp, $symbols) ))
109+
end
110+
111+
function _make_modules(exprs)
112+
uselst = Expr[]
113+
modlst = Symbol[]
114+
for ex in exprs
115+
if isa(ex, Expr) && ex.head == :tuple
116+
append!(modlst, ex.args)
117+
for e in ex.args ; push!(uselst, :(using $sym)) ; end
118+
elseif isa(ex, Symbol)
119+
push!(modlst, ex)
120+
push!(uselst, :(using $ex))
121+
else
122+
error("@api $cmd: syntax error $ex")
123+
end
124+
end
125+
uselst, modlst
126+
end
127+
128+
macro api(cmd::Symbol, exprs...)
129+
ind = _ff(_cmdadd, cmd)
130+
ind == 0 || return _add_symbols(cmd, exprs)
131+
132+
ind = _ff(_cmduse, cmd)
133+
ind == 0 && error("@api unrecognized command: $cmd")
134+
135+
lst, modules = _make_modules(exprs)
136+
137+
if ind == 1 # use
138+
grplst = (:public, :define_public)
139+
elseif ind == 2 # test
140+
grplst = (:public, :define_public, :develop, :define_develop)
141+
elseif ind == 3 # extend
142+
grplst = (:define_public, :define_develop)
143+
for mod in modules, grp in (:base, :public, :develop)
144+
push!(lst, _make_exprs(:import, mod, grp))
145+
end
146+
else # export
147+
grplst = ()
148+
for mod in modules, grp in (:public, :define_public)
149+
push!(lst, :(eval(Expr( :export, $mod.__api__.$grp... ))))
150+
end
151+
end
152+
for mod in modules, grp in grplst
153+
push!(lst, _make_exprs(:using, mod, grp))
154+
end
155+
esc(Expr(:toplevel, lst...))
156+
end
157+
158+
# We need Expr(:toplevel, (Expr($cmd, $mod, $sym) for sym in $mod.__api__.$grp)...)
159+
160+
function _make_list(cmd, mod, lst)
161+
isempty(lst) && return nothing
162+
@static if VERSION < v"0.7.0-DEV"
163+
length(lst) > 1 ?
164+
Expr(:toplevel, [Expr(cmd, mod, nam) for nam in lst]...) : Expr(cmd, mod, lst[1])
165+
else
166+
Expr(cmd, Expr(:(:), Expr(:., mod), [Expr(:., nam) for nam in lst]...))
167+
end
168+
end
169+
170+
function _make_exprs(cmd, mod, grp)
171+
from = QuoteNode(grp == :base ? :Base : mod)
172+
:(eval($(Meta.parse("APITools._make_list($(QuoteNode(cmd)), $from, $mod.__api__.$grp)"))))
173+
end
174+
175+
end # module APITools

test/APITest.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module APITest
2+
using APITools
3+
@api init
4+
@api base nextind, getindex, setindex!
5+
@api public myfunc
6+
@api define_public Foo
7+
8+
struct Foo end
9+
10+
function myfunc end
11+
myfunc(::Integer) = 1
12+
myfunc(::String) = 2
13+
14+
@api freeze
15+
end # module APITest

test/runtests.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Copyright 2018 Gandalf Software, Inc., Scott P. Jones
2+
# Licensed under MIT License, see LICENSE.md
3+
4+
using APITools
5+
6+
@static VERSION < v"0.7.0-DEV" ? (using Base.Test) : (using Test)
7+
8+
@def testcase begin
9+
myname = "Scott Paul Jones"
10+
end
11+
12+
@testset "@def macro" begin
13+
@testcase
14+
@test myname == "Scott Paul Jones"
15+
end
16+
17+
# Pick up APITest from the test directory
18+
push!(LOAD_PATH, @__DIR__)
19+
20+
@api init
21+
22+
@api extend APITest
23+
24+
myfunc(::AbstractFloat) = 3
25+
26+
@testset "@api macro" begin
27+
@test myfunc(1) == 1
28+
@test myfunc("foo") == 2
29+
@test myfunc(2.0) == 3
30+
@test APITest.__api__.mod == APITest
31+
@test APITest.__api__.base == (:nextind, :getindex, :setindex!)
32+
@test APITest.__api__.public == (:myfunc,)
33+
@test APITest.__api__.define_public == (:Foo,)
34+
end

0 commit comments

Comments
 (0)