Skip to content

Commit

Permalink
Create ArtifactWriter classes for writing ArtifactDefinitions
Browse files Browse the repository at this point in the history
  • Loading branch information
pidydx committed Jul 28, 2016
1 parent 5d0b934 commit 7bcab9f
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 18 deletions.
37 changes: 37 additions & 0 deletions artifacts/artifact.py
Expand Up @@ -59,3 +59,40 @@ def AppendSource(self, type_indicator, attributes):

self.sources.append(source_object)
return source_object

def AsDict(self):
"""Represents an artifact as a dictionary.
Returns:
A dictionary containing the artifact attributes.
"""
sources = []
for source in self.sources:
source_definition = {
u'type': source.type_indicator,
u'attributes': source.AsDict()
}
if source.supported_os:
source_definition[u'supported_os'] = source.supported_os
if source.conditions:
source_definition[u'conditions'] = source.conditions
if source.returned_types:
source_definition[u'returned_types'] = source.returned_types
sources.append(source_definition)

artifact_definition = {
u'name': self.name,
u'doc': self.description,
u'sources': sources,
}
if self.labels:
artifact_definition[u'labels'] = self.labels
if self.supported_os:
artifact_definition[u'supported_os'] = self.supported_os
if self.provides:
artifact_definition[u'provides'] = self.provides
if self.conditions:
artifact_definition[u'conditions'] = self.conditions
if self.urls:
artifact_definition[u'urls'] = self.urls
return artifact_definition
17 changes: 17 additions & 0 deletions artifacts/py2to3.py
@@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
"""The Python 2 and 3 compatible type definitions."""

import sys

if sys.version_info[0] < 3:
BYTES_TYPE = str
INTEGER_TYPES = (int, long)
LONG_TYPE = long
STRING_TYPES = (basestring,)
UNICODE_TYPE = unicode
else:
BYTES_TYPE = bytes
INTEGER_TYPES = (int,)
LONG_TYPE = int
STRING_TYPES = (str,)
UNICODE_TYPE = str
36 changes: 18 additions & 18 deletions artifacts/source_type.py
Expand Up @@ -42,8 +42,8 @@ def type_indicator(self):
return self.TYPE_INDICATOR

@abc.abstractmethod
def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand All @@ -70,8 +70,8 @@ def __init__(self, names=None):
super(ArtifactGroupSourceType, self).__init__()
self.names = names

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -103,8 +103,8 @@ def __init__(self, paths=None, separator=u'/'):
self.paths = paths
self.separator = separator

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -138,8 +138,8 @@ def __init__(self, args=None, cmd=None):
self.args = args
self.cmd = cmd

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -171,8 +171,8 @@ def __init__(self, paths=None, separator=u'/'):
self.paths = paths
self.separator = separator

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -208,8 +208,8 @@ def __init__(self, paths=None, separator=u'/'):
self.paths = paths
self.separator = separator

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -255,8 +255,8 @@ def __init__(self, keys=None):
super(WindowsRegistryKeySourceType, self).__init__()
self.keys = keys

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -321,8 +321,8 @@ def __init__(self, key_value_pairs=None):
super(WindowsRegistryValueSourceType, self).__init__()
self.key_value_pairs = key_value_pairs

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down Expand Up @@ -351,8 +351,8 @@ def __init__(self, query=None, base_object=None):
self.base_object = base_object
self.query = query

def CopyToDict(self):
"""Copies the source type to a dictionary.
def AsDict(self):
"""Represents a source type as a dictionary.
Returns:
A dictionary containing the source type attributes.
Expand Down
96 changes: 96 additions & 0 deletions artifacts/writer.py
@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
"""The artifact writer objects."""

import abc
import json
import yaml

import artifacts.py2to3 as py2to3
from artifacts.artifact import ArtifactDefinition


class BaseArtifactsWriter(object):
"""Class that implements the artifacts writer interface."""

@abc.abstractmethod
def WriteArtifactsFile(self, artifacts, filename):
"""Writes artifact definitions to a file.
Args:
artifacts: a list of ArtifactDefinition objects to be written.
filename: the filename to write artifacts to.
"""

@abc.abstractmethod
def FormatArtifacts(self, artifacts):
"""Formats artifacts to desired output format.
Args:
artifacts: an ArtifactDefinition instance or list of ArtifactDefinitions.
Returns:
formatted string of artifact definition.
"""


class ArtifactWriter(BaseArtifactsWriter):
"""Class that implements the artifacts writer interface."""

def WriteArtifactsFile(self, artifacts, filename):
"""Writes artifact definitions to a file.
Args:
artifacts: a list of ArtifactDefinition objects to be written.
filename: the filename to write artifacts to.
"""
with open(filename, 'wb') as file_object:
file_object.write(self.FormatArtifacts(artifacts))


class JsonArtifactsWriter(ArtifactWriter):
"""Class that implements the JSON artifacts writer interface."""

def FormatArtifacts(self, artifacts):
"""Formats artifacts to desired output format.
Args:
artifacts: a list of ArtifactDefinitions.
Returns:
formatted string of artifact definition.
"""
artifact_definitions = [artifact.AsDict() for artifact in artifacts]
json_data = json.dumps(artifact_definitions)
if isinstance(json_data, py2to3.BYTES_TYPE):
try:
return json_data.decode('ascii')
except UnicodeDecodeError:
pass
else:
return json_data


class YamlArtifactsWriter(ArtifactWriter):
"""Class that implements the YAML artifacts writer interface."""

def FormatArtifacts(self, artifacts):
"""Formats artifacts to desired output format.
Args:
artifacts: a list of ArtifactDefinitions.
Returns:
formatted string of artifact definition.
"""
# TODO: improve output formatting of yaml
artifact_definitions = [artifact.AsDict() for artifact in artifacts]
yaml_data = yaml.safe_dump_all(artifact_definitions)
if isinstance(yaml_data, py2to3.BYTES_TYPE):
try:
return yaml_data.decode('ascii')
except UnicodeDecodeError:
pass
else:
return yaml_data
29 changes: 29 additions & 0 deletions tests/reader_test.py
Expand Up @@ -4,6 +4,7 @@
import io
import os
import unittest
import yaml

from artifacts import definitions
from artifacts import errors
Expand Down Expand Up @@ -267,6 +268,34 @@ def testReadDirectory(self):

self.assertEqual(len(artifact_definitions), 7)

def testArtifactAsDict(self):
"""Tests the ArtifactDefinition AsDict method returns the same dict that parsing artifact from yaml yields."""
artifact_reader = reader.YamlArtifactsReader()
test_file = os.path.join('test_data', 'definitions.yaml')

with open(test_file, 'r') as file_object:
for artifact_definition in yaml.safe_load_all(file_object):
artifact_object = artifact_reader.ReadArtifactDefinitionValues(
artifact_definition)
self.assertEqual(artifact_definition, artifact_object.AsDict())

def testDefinitionsAsDict(self):
"""Tests that all defined artifacts can convert to dictionary representation without raising."""
artifact_reader = reader.YamlArtifactsReader()

artifact_definitions = list(artifact_reader.ReadDirectory('definitions'))

last_artifact_definition = None
for artifact in artifact_definitions:
try:
artifact_definition = artifact.AsDict()
except errors.FormatError:
error_location = u'At start'
if last_artifact_definition:
error_location = u'After: {0}'.format(last_artifact_definition.name)
self.fail(u'{0} failed to convert to dict'.format(error_location))
last_artifact_definition = artifact_definition


if __name__ == '__main__':
unittest.main()
57 changes: 57 additions & 0 deletions tests/writer_test.py
@@ -0,0 +1,57 @@
# -*- coding: utf-8 -*-
"""Tests for the artifact definitions readers."""

import io
import os
import unittest
import json

from artifacts import reader
from artifacts import writer


class YamlArtifactsWriterTest(unittest.TestCase):
"""Class to test the YAML artifacts writer"""

def testYamlWriter(self):
"""Tests the YamlArtifactsWriter FormatArtifacts method for loss during conversion."""
artifact_reader = reader.YamlArtifactsReader()
artifact_writer = writer.YamlArtifactsWriter()
test_file = os.path.join('test_data', 'definitions.yaml')

artifact_definitions = list(artifact_reader.ReadFile(test_file))
artifacts_yaml = artifact_writer.FormatArtifacts(artifact_definitions)

file_object = io.StringIO(initial_value=artifacts_yaml)
converted_artifact_definitions = list(artifact_reader.ReadFileObject(
file_object))

self.assertListEqual(
[artifact.AsDict() for artifact in artifact_definitions],
[artifact.AsDict() for artifact in converted_artifact_definitions])


class JsonArtifactsWriterTest(unittest.TestCase):
"""Class to test the JSON artifacts writer"""

def testJsonWriter(self):
"""Tests the JsonArtifactsWriter FormatArtifacts method for loss during conversion."""
artifact_reader = reader.YamlArtifactsReader()
artifact_writer = writer.JsonArtifactsWriter()
test_file = os.path.join('test_data', 'definitions.yaml')

artifact_definitions = list(artifact_reader.ReadFile(test_file))
artifacts_json = artifact_writer.FormatArtifacts(artifact_definitions)

converted_artifact_definitions = [
artifact_reader.ReadArtifactDefinitionValues(artifact_definition)
for artifact_definition in json.loads(artifacts_json)
]

self.assertListEqual(
[artifact.AsDict() for artifact in artifact_definitions],
[artifact.AsDict() for artifact in converted_artifact_definitions])


if __name__ == '__main__':
unittest.main()

0 comments on commit 7bcab9f

Please sign in to comment.