Skip to content

Commit

Permalink
Rule name Improvements (#45)
Browse files Browse the repository at this point in the history
* Renamed the example directory in the test directory to valid

* Updated the grammer to allow dashes to be used in rule names

* Updated the grammer to allow for keys with spaces if they are enclosed with quotes. Updated the parsing test case schema file with different names to confirm the regex works

* Updated the parser rule_name to have test cases. Replaced the NamedTuple
Token with the actual Lark type

* Updated the documentation to include details on the update rule names. Updated the changelog with the release date and set the new version in the setup.py file
  • Loading branch information
ryan95f authored Jan 15, 2023
1 parent 580b15a commit 05b22fc
Show file tree
Hide file tree
Showing 15 changed files with 135 additions and 77 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/publish.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ on:
- 'v*'
jobs:
build-and-publish:
runs-on: ubuntu-18.04
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.9
Expand All @@ -21,6 +21,6 @@ jobs:
python setup.py sdist bdist_wheel
- name: Publish to PyPI
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@master
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_API_TOKEN }}
5 changes: 5 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,8 @@
* Added new example [strict_mode](./example/strict_mode/) to the `example` directory
* Updated GitHub workloads to use the latest actions
* Fixed bug with rule definitions where field names with similar character patterns to types would create two separate rules

## v0.2.1 (15th January 2023)

* Improved the Yamlator grammar to allow rule names to accept options including symbols, unicode, spaces and dashes. Any YAML key that contains spaces, must be enclosed in double quotes
* Upgraded the Ubuntu version on the publish workflow
14 changes: 14 additions & 0 deletions docs/schema_components.md
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,20 @@ ruleset <ruleset-name> {

Required rules validate that the key is present in the YAML data. If the required data is missing then a required violation is raised. If the rule is optional, then a violation is not raised when it is missing from the YAML data.

Rule names support dashes, underscores and all unicode characters. If the field name in YAML contains spaces, e.g `my awesome field`, then the rule name will need to be enclosed in double quotes. For example, given the following YAML data:

```yaml
my awesome field: 42
```

The corresponding rule name in a ruleset:

```text
ruleset <ruleset-name> {
"my awesome field" int
}
```

## Rule Types

For each rule, a type can be specified to validate the expected data type is present.
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import setuptools

VERSION = '0.2.0'
VERSION = '0.2.1'
PACKAGE_NAME = 'yamlator'
DESCRIPTION = 'Yamlator is a CLI tool that allows a YAML file to be validated using a lightweight schema language' # nopep8

Expand Down
6 changes: 3 additions & 3 deletions tests/cmd/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
from yamlator.cmd import DisplayMethod
from yamlator.cmd.outputs import SuccessCode

HELLO_YAML_FILE_PATH = './tests/files/example/example.yaml'
HELLO_RULESET_FILE_PATH = './tests/files/example/example.ys'
INVALID_HELLO_YAML_FILE_PATH = './tests/files/example/invalid_example.yaml'
HELLO_YAML_FILE_PATH = './tests/files/valid/valid.yaml'
HELLO_RULESET_FILE_PATH = './tests/files/valid/valid.ys'
INVALID_HELLO_YAML_FILE_PATH = './tests/files/valid/invalid.yaml'

ValidateArgs = namedtuple('ValidateArgs', ['file', 'ruleset_schema', 'output'])

Expand Down
4 changes: 2 additions & 2 deletions tests/cmd/test_validate_yaml_data_from_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
from yamlator.exceptions import InvalidSchemaFilenameError

EMPTY_STR = ''
VALID_YAML_DATA_FILE_PATH = './tests/files/example/example.yaml'
VALID_SCHEMA_FILE_PATH = './tests/files/example/example.ys'
VALID_YAML_DATA_FILE_PATH = './tests/files/valid/valid.yaml'
VALID_SCHEMA_FILE_PATH = './tests/files/valid/valid.ys'

ValidateArgs = namedtuple('ValidateArgs', ['yaml_filepath', 'schema_filepath'])

Expand Down
26 changes: 0 additions & 26 deletions tests/files/example/example.ys

This file was deleted.

File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
message: hello world
number: 42
person:
name: Test Tester
first_name: Test
last-name: Tester
age: 42
isEmployed: true
department: lead
department: lead
39 changes: 39 additions & 0 deletions tests/files/valid/valid.ys
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Test the use of underscores
enum Employee_department {
MANAGER = "manager"
LEAD = "lead"
}

ruleset PersonAddress {
houseNumber int
street str
city str
post_code str
}

ruleset Person {
first_name str # Make sure underscores are parsed correctly
last-name str # Make sure dashes are parsed correctly
age int
address PersonAddress optional
isEmployed bool
department Employee_department
}

# Testing different field options
ruleset FieldOptions {
under_scores str
required-under_scores str required
required_under-scores str optional
"hello world" str optional
ரஷ str required
ரஷ3 str
!!test[] str optional
}

schema {
message str
number int
person Person optional
options FieldOptions optional
}
6 changes: 3 additions & 3 deletions tests/parser/test_parse_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ class TestParseSchema(unittest.TestCase):
"""Tests the parse schema function"""

def setUp(self):
self.valid_schema_file = './tests/files/example/example.ys'
self.invalid_schema_file = './tests/files/example/example.yaml'
self.valid_schema_file = './tests/files/valid/valid.ys'
self.invalid_schema_file = './tests/files/valid/valid.yaml'

def test_parse_with_none_text(self):
with self.assertRaises(ValueError):
Expand Down Expand Up @@ -78,7 +78,7 @@ def test_parse_with_valid_content(self):
),
(
'with_invalid_schema_syntax',
'./tests/files/example/example.yaml',
'./tests/files/valid/valid.yaml',
SchemaSyntaxError
)
])
Expand Down
33 changes: 22 additions & 11 deletions tests/parser/test_schema_transformer.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@

import re
import unittest
import lark

from collections import namedtuple
from parameterized import parameterized
from yamlator.exceptions import ConstructNotFoundError

Expand All @@ -63,23 +63,34 @@
from yamlator.types import YamlatorEnum, YamlatorRuleset, SchemaTypes


Token = namedtuple('Token', ['value'])


class TestSchemaTransformer(unittest.TestCase):
"""Tests the Schema Transformer"""

def setUp(self):
self.transformer = SchemaTransformer()
self.name_token = Token('message')
self.status_code_token = Token('StatusCode')
self.name_token = lark.Token(type_='NAME', value='message')
self.status_code_token = lark.Token(type_='STATUS', value='StatusCode')

self.str_rtype = RuleType(schema_type=SchemaTypes.STR)
self.ruleset_rules = [
Rule('name', RuleType(schema_type=SchemaTypes.STR), True),
Rule('age', RuleType(schema_type=SchemaTypes.INT), True),
]

@parameterized.expand([
('without_quotes', 'ruleName', 'ruleName'),
('with_double_quotes', '\"ruleName\"', 'ruleName'),
('with_single_quotes', '\'ruleName\'', 'ruleName'),
('with_spaces', 'rule name', 'rule name'),
('with_padding_spaces', ' rule name ', 'rule name'),
])
def test_rule_name(self, name: str, rule_name: str, expected: str):
# Unused by test case, however is required by the parameterized library
del name
token = lark.Token('TOKEN', value=rule_name)
processed_token = self.transformer.rule_name([token])
self.assertEqual(expected, processed_token.value)

def test_required_rule(self):
tokens = (self.name_token, self.str_rtype)

Expand All @@ -95,7 +106,7 @@ def test_optional_rule(self):
self.assertFalse(optional_rule.is_required)

def test_ruleset(self):
name = Token('person')
name = lark.Token(type_='TOKEN', value='person')
tokens = (name, *self.ruleset_rules)
ruleset = self.transformer.ruleset(tokens)

Expand All @@ -104,7 +115,7 @@ def test_ruleset(self):
self.assertFalse(ruleset.is_strict)

def test_strict_ruleset(self):
name = Token('person')
name = lark.Token(type_='TOKEN', value='person')
tokens = (name, *self.ruleset_rules)
ruleset = self.transformer.strict_ruleset(tokens)

Expand Down Expand Up @@ -193,9 +204,9 @@ def test_enum(self):
self.assertEqual(len(enum_items), len(enum.items))

def test_container_type(self):
token = Token('Employee')
token = lark.Token(type_='TOKEN', value='Employee')
self.transformer.seen_constructs = {'Employee': SchemaTypes.RULESET}
rule = self.transformer.container_type(token)
rule = self.transformer.container_type([token])
self.assertEqual(rule.schema_type, SchemaTypes.RULESET)

@parameterized.expand([
Expand All @@ -207,7 +218,7 @@ def test_container_type(self):
])
def test_container_type_construct_does_not_exist(self, name: str,
seen_constructs: dict):
token = Token(name)
token = lark.Token(type_='TOKEN', value=name)
self.transformer.seen_constructs = seen_constructs
with self.assertRaises(ConstructNotFoundError):
self.transformer.container_type(token)
Expand Down
6 changes: 3 additions & 3 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def test_yaml_file_invalid_filename(self, name: str, filename: str,
load_yaml_file(filename)

@parameterized.expand([
('yaml_file', 'tests/files/example/example.yaml')
('yaml_file', 'tests/files/valid/valid.yaml')
])
def test_load_yaml_file(self, name: str, filename: str):
# Unused by test case, however is required by the parameterized library
Expand Down Expand Up @@ -65,8 +65,8 @@ def test_load_schema_with_invalid_filename(self, name: str, filename: str,
load_schema(filename)

@parameterized.expand([
('with_unix_style_path', 'tests/files/example/example.ys'),
('with_windows_style_path', 'tests\\files\\example\\example.ys')
('with_unix_style_path', 'tests/files/valid/valid.ys'),
('with_windows_style_path', 'tests\\files\\valid\\valid.ys')
])
def test_successfully_load_schema(self, name, filename):
# Unused by test case, however is required by the parameterized library
Expand Down
9 changes: 6 additions & 3 deletions yamlator/grammar/grammar.lark
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,12 @@ enum_item: /[A-Z0-9_]+/ "=" values
?rule: required_rule
| optional_rule

required_rule: /[a-zA-Z0-9_]+/ type "required" NEW_LINES
| /[a-zA-Z0-9_]+/ type NEW_LINES
optional_rule: /[a-zA-Z0-9_]+/ type "optional" NEW_LINES
rule_name: /[\S]+/
| /\"[\S ]+\"/

required_rule: rule_name type "required" NEW_LINES
| rule_name type NEW_LINES
optional_rule: rule_name type "optional" NEW_LINES

// Data types for rules
type: int_type
Expand Down
Loading

0 comments on commit 05b22fc

Please sign in to comment.