Parse lines from a Chef `metadata.rb` file. For example, a field in `metadata.rb` can look like this: name "python" `RubyLexer()` interprets the line as so: ['Token.Name.Builtin', "u'name'"], ['Token.Text', "u' '"],
(self, tokens, outfile)
| 72 | class ChefMetadataFormatter(Formatter): |
| 73 | |
| 74 | def format(self, tokens, outfile): |
| 75 | """ |
| 76 | Parse lines from a Chef `metadata.rb` file. |
| 77 | |
| 78 | For example, a field in `metadata.rb` can look like this: |
| 79 | |
| 80 | name "python" |
| 81 | |
| 82 | `RubyLexer()` interprets the line as so: |
| 83 | |
| 84 | ['Token.Name.Builtin', "u'name'"], |
| 85 | ['Token.Text', "u' '"], |
| 86 | ['Token.Literal.String.Double', 'u\'"\''], |
| 87 | ['Token.Literal.String.Double', "u'python'"], |
| 88 | ['Token.Literal.String.Double', 'u\'"\''], |
| 89 | ['Token.Text', "u'\\n'"] |
| 90 | |
| 91 | With this pattern of tokens, we iterate through the token stream to |
| 92 | create a dictionary whose keys are the variable names from `metadata.rb` |
| 93 | and its values are those variable's values. This dictionary is then dumped |
| 94 | to `outfile` as JSON. |
| 95 | """ |
| 96 | metadata = dict(depends={}) |
| 97 | line = [] |
| 98 | identifiers_and_literals = ( |
| 99 | Token.Name, |
| 100 | Token.Name.Builtin, # NOQA |
| 101 | Token.Punctuation, |
| 102 | Token.Literal.String.Single, # NOQA |
| 103 | Token.Literal.String.Double # NOQA |
| 104 | ) |
| 105 | quotes = '"', "'" |
| 106 | quoted = lambda x: (x.startswith('"') and x.endswith('"')) or (x.startswith("'") and value.endswith("'")) |
| 107 | |
| 108 | for ttype, value in tokens: |
| 109 | # We don't allow tokens that are just '\"' or '\'' |
| 110 | if (ttype in identifiers_and_literals and value not in quotes): |
| 111 | # Some tokens are strings with leading and trailing quotes, so |
| 112 | # we remove them |
| 113 | if quoted(value): |
| 114 | value = value[1:-1] |
| 115 | line.append(value) |
| 116 | |
| 117 | if ttype in (Token.Text,) and value.endswith('\n') and line: |
| 118 | # The field name should be the first element in the list |
| 119 | key = line.pop(0) |
| 120 | # Join all tokens as a single string |
| 121 | joined_line = ''.join(line) |
| 122 | |
| 123 | # Store dependencies as dependency_name:dependency_requirement |
| 124 | # in an Object instead of a single string |
| 125 | if key == 'depends': |
| 126 | # Dependencies are listed in the form of dependency,requirement |
| 127 | dep_requirement = joined_line.rsplit(',') |
| 128 | if len(dep_requirement) == 2: |
| 129 | dep_name = dep_requirement[0] |
| 130 | requirement = dep_requirement[1] |
| 131 | else: |