Check for required args first, and check for them all at once.

This is necessary for versions of Python <3.6, where dictionaries are
unordered by default, meaning we can't ensure the order in which fields
are checked.
stix2.1
Greg Back 2017-02-02 10:16:10 -06:00
parent 1ba064734b
commit 5d7ed643bd
3 changed files with 24 additions and 24 deletions

View File

@ -61,7 +61,7 @@ will result in an error:
```python
>>> indicator = Indicator()
ValueError: Missing required field for Indicator: 'labels'
ValueError: Missing required field(s) for Indicator: (labels, pattern).
```
However, the required `valid_from` attribute on Indicators will be set to the

View File

@ -74,12 +74,15 @@ class _STIXBase(collections.Mapping):
if extra_kwargs:
raise TypeError("unexpected keyword arguments: " + str(extra_kwargs))
required_fields = [k for k, v in cls._properties.items() if v.get('required')]
missing_kwargs = set(required_fields) - set(kwargs)
if missing_kwargs:
msg = "Missing required field(s) for {type}: ({fields})."
field_list = ", ".join(x for x in sorted(list(missing_kwargs)))
raise ValueError(msg.format(type=class_name, fields=field_list))
for prop_name, prop_metadata in cls._properties.items():
if prop_name not in kwargs:
if prop_metadata.get('required'):
msg = "Missing required field for {type}: '{field}'."
raise ValueError(msg.format(type=class_name,
field=prop_name))
if prop_metadata.get('default'):
default = prop_metadata['default']
if default == NOW:

View File

@ -150,29 +150,28 @@ def test_indicator_autogenerated_fields(indicator):
def test_indicator_type_must_be_indicator():
with pytest.raises(ValueError) as excinfo:
indicator = stix2.Indicator(type='xxx')
indicator = stix2.Indicator(type='xxx', **INDICATOR_KWARGS)
assert "Indicator must have type='indicator'." in str(excinfo)
def test_indicator_id_must_start_with_indicator():
with pytest.raises(ValueError) as excinfo:
indicator = stix2.Indicator(id='my-prefix--')
indicator = stix2.Indicator(id='my-prefix--', **INDICATOR_KWARGS)
assert "Indicator id values must begin with 'indicator--'." in str(excinfo)
def test_indicator_required_field_labels():
def test_indicator_required_fields():
with pytest.raises(ValueError) as excinfo:
indicator = stix2.Indicator()
assert "Missing required field for Indicator: 'labels'." in str(excinfo)
assert "Missing required field(s) for Indicator: (labels, pattern)." in str(excinfo)
def test_indicator_required_field_pattern():
with pytest.raises(ValueError) as excinfo:
# Label is checked first, so make sure that is provided
indicator = stix2.Indicator(labels=['malicious-activity'])
assert "Missing required field for Indicator: 'pattern'." in str(excinfo)
assert "Missing required field(s) for Indicator: (pattern)." in str(excinfo)
def test_cannot_assign_to_indicator_attributes(indicator):
@ -240,29 +239,28 @@ def test_malware_autogenerated_fields(malware):
def test_malware_type_must_be_malware():
with pytest.raises(ValueError) as excinfo:
malware = stix2.Malware(type='xxx')
malware = stix2.Malware(type='xxx', **MALWARE_KWARGS)
assert "Malware must have type='malware'." in str(excinfo)
def test_malware_id_must_start_with_malware():
with pytest.raises(ValueError) as excinfo:
malware = stix2.Malware(id='my-prefix--')
malware = stix2.Malware(id='my-prefix--', **MALWARE_KWARGS)
assert "Malware id values must begin with 'malware--'." in str(excinfo)
def test_malware_required_field_labels():
def test_malware_required_fields():
with pytest.raises(ValueError) as excinfo:
malware = stix2.Malware()
assert "Missing required field for Malware: 'labels'." in str(excinfo)
assert "Missing required field(s) for Malware: (labels, name)." in str(excinfo)
def test_malware_required_field_name():
with pytest.raises(ValueError) as excinfo:
# Label is checked first, so make sure that is provided
malware = stix2.Malware(labels=['ransomware'])
assert "Missing required field for Malware: 'name'." in str(excinfo)
assert "Missing required field(s) for Malware: (name)." in str(excinfo)
def test_cannot_assign_to_malware_attributes(malware):
@ -324,14 +322,14 @@ def test_relationship_autogenerated_fields(relationship):
def test_relationship_type_must_be_relationship():
with pytest.raises(ValueError) as excinfo:
relationship = stix2.Relationship(type='xxx')
relationship = stix2.Relationship(type='xxx', **RELATIONSHIP_KWARGS)
assert "Relationship must have type='relationship'." in str(excinfo)
def test_relationship_id_must_start_with_relationship():
with pytest.raises(ValueError) as excinfo:
relationship = stix2.Relationship(id='my-prefix--')
relationship = stix2.Relationship(id='my-prefix--', **RELATIONSHIP_KWARGS)
assert "Relationship id values must begin with 'relationship--'." in str(excinfo)
@ -339,24 +337,23 @@ def test_relationship_id_must_start_with_relationship():
def test_relationship_required_field_relationship_type():
with pytest.raises(ValueError) as excinfo:
relationship = stix2.Relationship()
assert "Missing required field for Relationship: 'relationship_type'." in str(excinfo)
assert "Missing required field(s) for Relationship: (relationship_type, source_ref, target_ref)." in str(excinfo)
def test_relationship_required_field_source_ref():
def test_relationship_missing_some_required_fields():
with pytest.raises(ValueError) as excinfo:
# relationship_type is checked first, so make sure that is provided
relationship = stix2.Relationship(relationship_type='indicates')
assert "Missing required field for Relationship: 'source_ref'." in str(excinfo)
assert "Missing required field(s) for Relationship: (source_ref, target_ref)." in str(excinfo)
def test_relationship_required_field_target_ref():
with pytest.raises(ValueError) as excinfo:
# relationship_type and source_ref are checked first, so make sure those are provided
relationship = stix2.Relationship(
relationship_type='indicates',
source_ref=INDICATOR_ID
)
assert "Missing required field for Relationship: 'target_ref'." in str(excinfo)
assert "Missing required field(s) for Relationship: (target_ref)." in str(excinfo)
def test_cannot_assign_to_relationship_attributes(relationship):