Skip to content

Commit 7ff8206

Browse files
earlbreaddahlia
authored andcommitted
Add numeric-constraints annotation
1 parent c3eea56 commit 7ff8206

File tree

6 files changed

+94
-8
lines changed

6 files changed

+94
-8
lines changed

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ To be released.
1010

1111
- The `uri` type has completly gone; use `url` instead.
1212
[[#126], [#281] by Jonghun Park]
13+
- Added constraints for numeric unboxed types. [[#206], [#271]]
1314

1415
### Docs target
1516

@@ -57,7 +58,9 @@ To be released.
5758
(from [spoqa/nirum](https://hub.docker.com/r/spoqa/nirum/)).
5859

5960
[#126]: https://github.com/nirum-lang/nirum/issues/126
61+
[#206]: https://github.com/nirum-lang/nirum/issues/206
6062
[#225]: https://github.com/nirum-lang/nirum/issues/225
63+
[#271]: https://github.com/nirum-lang/nirum/pull/271
6164
[#281]: https://github.com/nirum-lang/nirum/pull/281
6265
[#283]: https://github.com/spoqa/nirum/pull/283
6366
[#297]: https://github.com/nirum-lang/nirum/issues/297

src/Nirum/Targets/Python.hs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module Nirum.Targets.Python
1818
import Control.Monad (forM)
1919
import Control.Monad.State (modify)
2020
import qualified Data.List as L
21-
import Data.Maybe (catMaybes, fromMaybe)
21+
import Data.Maybe (catMaybes, fromMaybe, isJust)
2222
import GHC.Exts (IsList (toList))
2323

2424
import qualified Data.ByteString.Lazy
@@ -685,12 +685,33 @@ compileTypeDeclaration src d@TypeDeclaration { typename = typename'
685685
|]
686686
compileTypeDeclaration src d@TypeDeclaration { typename = typename'
687687
, type' = UnboxedType itype
688+
, typeAnnotations = annots
688689
} = do
689690
let className = toClassName' typename'
690691
itypeExpr <- compileTypeExpression' src (Just itype)
691692
insertStandardImport "typing"
692693
pyVer <- getPythonVersion
693-
Validator typePred valueValidators' <- compileValidator' src itype "value"
694+
Validator typePred valueValidatorsProto <-
695+
compileValidator' src itype "value"
696+
valueValidators' <- case A.lookup "numeric-constraints" annots of
697+
Just A.Annotation { A.arguments = args } -> do
698+
let constraintValidators =
699+
[ case (name', value) of
700+
("min", Integer v) ->
701+
Just $ ValueValidator
702+
[qq|value >= ($v)|]
703+
[qq|value is less than $v|]
704+
("max", Integer v) ->
705+
Just $ ValueValidator
706+
[qq|value <= ($v)|]
707+
[qq|value is greater than $v|]
708+
_ -> Nothing
709+
| (name', value) <- toList args
710+
]
711+
if all isJust constraintValidators
712+
then return $ catMaybes constraintValidators
713+
else fail "Unsupported arguments on @numeric-constraints"
714+
Nothing -> return valueValidatorsProto
694715
deserializer <- compileDeserializer' src itype "value" "rv" "on_error"
695716
defaultErrorHandler <- defaultDeserializerErrorHandler
696717
return [compileText|

test/Nirum/Targets/PythonSpec.hs

Lines changed: 49 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33
module Nirum.Targets.PythonSpec where
44

55
import qualified Data.Map.Strict as M
6+
import Data.Either
67
import System.FilePath ((</>))
78
import Test.Hspec.Meta
89

10+
import Nirum.Constructs.Annotation hiding (null)
11+
import Nirum.Constructs.Annotation.Internal
912
import Nirum.Constructs.Module (Module (Module))
10-
import Nirum.Package.Metadata (Target (compilePackage))
13+
import Nirum.Constructs.TypeDeclaration hiding (Text)
14+
import qualified Nirum.Package.Metadata as M
1115
import Nirum.Targets.Python
1216
( Source (Source)
1317
, parseModulePath
@@ -19,7 +23,7 @@ spec = do
1923
describe "compilePackage" $ do
2024
it "returns a Map of file paths and their contents to generate" $ do
2125
let (Source pkg _) = makeDummySource $ Module [] Nothing
22-
files = compilePackage pkg
26+
files = M.compilePackage pkg
2327
directoryStructure =
2428
[ "src-py2" </> "foo" </> "__init__.py"
2529
, "src-py2" </> "foo" </> "bar" </> "__init__.py"
@@ -34,7 +38,7 @@ spec = do
3438
it "creates an emtpy Python package directory if necessary" $ do
3539
let (Source pkg _) = makeDummySource' ["test"] (Module [] Nothing)
3640
[]
37-
files = compilePackage pkg
41+
files = M.compilePackage pkg
3842
directoryStructure =
3943
[ "src-py2" </> "test" </> "__init__.py"
4044
, "src-py2" </> "test" </> "foo" </> "__init__.py"
@@ -51,7 +55,7 @@ spec = do
5155
it "generates renamed package dirs if renames are configured" $ do
5256
let (Source pkg _) = makeDummySource' [] (Module [] Nothing)
5357
[(["foo"], ["quz"])]
54-
files = compilePackage pkg
58+
files = M.compilePackage pkg
5559
directoryStructure =
5660
[ "src-py2" </> "quz" </> "__init__.py"
5761
, "src-py2" </> "quz" </> "bar" </> "__init__.py"
@@ -65,7 +69,7 @@ spec = do
6569
M.keysSet files `shouldBe` directoryStructure
6670
let (Source pkg' _) = makeDummySource' [] (Module [] Nothing)
6771
[(["foo", "bar"], ["bar"])]
68-
files' = compilePackage pkg'
72+
files' = M.compilePackage pkg'
6973
directoryStructure' =
7074
[ "src-py2" </> "foo" </> "__init__.py"
7175
, "src-py2" </> "bar" </> "__init__.py"
@@ -90,3 +94,43 @@ spec = do
9094
parseModulePath "foo..bar" `shouldBe` Nothing
9195
parseModulePath "foo.bar>" `shouldBe` Nothing
9296
parseModulePath "foo.bar-" `shouldBe` Nothing
97+
98+
describe "@numeric-constraints" $ do
99+
it "fails if unsupported arguments are present" $ do
100+
let Right annots = fromList
101+
[ Annotation
102+
"numeric-constraints"
103+
[("min", Integer 1), ("unsupported", Integer 2)]
104+
]
105+
compareErrorMessage annots
106+
107+
it "fails if unsupported arguments type is given" $ do
108+
let Right annots = fromList
109+
[ Annotation
110+
"numeric-constraints"
111+
[("min", Integer 1), ("max", Text "2")]
112+
]
113+
compareErrorMessage annots
114+
115+
it "success" $ do
116+
let Right annots = fromList
117+
[ Annotation
118+
"numeric-constraints"
119+
[("min", Integer 1), ("max", Integer 2)]
120+
]
121+
let Just result = getResult annots
122+
isRight result `shouldBe` True
123+
where
124+
compareErrorMessage annots = do
125+
let Just result = getResult annots
126+
let Left errorMessage = result
127+
errorMessage `shouldBe`
128+
"Unsupported arguments on @numeric-constraints"
129+
130+
getResult annots =
131+
let
132+
unboxed = TypeDeclaration "foo" (UnboxedType "int32") annots
133+
(Source pkg _) = makeDummySource $ Module [unboxed] Nothing
134+
files = M.compilePackage pkg
135+
in
136+
M.lookup ("src" </> "foo" </> "__init__.py") files
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
@numeric-constraints(min=1, max=12)
2+
unboxed month (int32);

test/python/constraints_test.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# -*- coding: utf-8 -*-
2+
from pytest import raises
3+
4+
from fixture.constraints import Month
5+
6+
7+
def test_numeric_constraints():
8+
month = Month(1)
9+
assert month.value == 1
10+
11+
with raises(ValueError):
12+
Month(0)
13+
14+
with raises(ValueError):
15+
Month(13)

test/python/setup_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def test_setup_metadata():
2222
assert set(pkg['Provides']) == {
2323
'fixture', 'fixture.foo', 'fixture.foo.bar', 'fixture.qux',
2424
'fixture.reserved_keyword_enum', 'fixture.reserved_keyword_union',
25-
'fixture.types', 'fixture.alias',
25+
'fixture.types', 'fixture.alias', 'fixture.constraints',
2626
'renamed', 'renamed.foo', 'renamed.foo.bar',
2727
'fixture.datetime',
2828
'fixture.name',
@@ -46,6 +46,7 @@ def test_module_entry_points():
4646
'fixture.reserved-keyword-enum', 'fixture.reserved-keyword-union',
4747
'fixture.types',
4848
'fixture.alias',
49+
'fixture.constraints',
4950
'renames.test.foo', 'renames.test.foo.bar',
5051
'fixture.datetime',
5152
'fixture.name',

0 commit comments

Comments
 (0)