diff --git a/README.md b/README.md index d3bee9a..cac43a4 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,21 @@ -# gemfileparser -Parse Ruby Gemfile's using Python. Supports Gemfiles and .gemspec files. +# gemfileparser2 +Parse Ruby Gemfile's using Python. Supports Gemfiles, .gemspec and Cocoapods(.podspec) files. Friendly fork of https://gitlab.com/balasankarc/gemfileparser. + +[gemfileparser](https://gitlab.com/balasankarc/gemfileparser) can only detect particular type of dependency in `.gemspec` files like it can detect only `s.add_development_dependency "rspec", "~>1.3.1"` or `s.add_runtime_dependency "rspec", "~>1.3.1"` type of dependency. Dependency should be in these 2 format only. +[gemfileparser2](https://github.com/nexB/gemfileparser2) can detect all format of dependencies. This fork supports Gemfiles, .gemspec files and Cocoapods(.podspec) files. ### Installation -If using pip, use the command `sudo pip install gemfileparser` +If using pip, use the command `sudo pip install gemfileparser2` Else use the following commands ``` -git clone https://github.com/balasankarc/gemfileparser.git -cd gemfileparser +git clone https://github.com/nexB/gemfileparser2.git +cd gemfileparser2 python setup.py install ``` ### Usage ``` -from gemfileparser import GemfileParser +from gemfileparser2 import GemfileParser parser = GemfileParser(, ) dependency_dictionary = parser.parse() ``` @@ -37,7 +40,7 @@ group - Group in which gem is a member of (default : runtime) #### Example ``` -from gemfileparser import GemfileParser +from gemfileparser2 import GemfileParser n = GemfileParser('Gemfile', 'diaspora') deps = n.parse() for key in deps: diff --git a/gemfileparser/__init__.py b/gemfileparser/__init__.py index 16aa69a..e9ae452 100644 --- a/gemfileparser/__init__.py +++ b/gemfileparser/__init__.py @@ -14,7 +14,9 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -'''Python library to parse Ruby's Gemfiles and gemspec files.''' +""" +Python library to parse Ruby's Gemfiles and gemspec files. +""" import csv import io @@ -25,65 +27,51 @@ class GemfileParser(object): - - '''Creates a GemfileParser object to perform operations. ''' + """ + Creates a GemfileParser object to perform operations. + """ class Dependency(object): - - ''' A class to hold information about a dependency gem.''' + """ + A class to hold information about a dependency gem. + """ def __init__(self): - self.name = None + self.name = '' self.requirement = [] - self.autorequire = None - self.source = None + self.autorequire = '' + self.source = '' self.parent = [] - self.group = None - self.platform = None - self.platforms = [] - self.groups = [] - - def __str__(self): - attributes = self.__dict__ - output = {} - for key, value in attributes.items(): - if value is None or value == []: - next - else: - output[key] = value - return str(output) + self.group = '' gemfile_regexes = collections.OrderedDict() - gemfile_regexes['name'] = re.compile(r"gem ['\"](?P.*?)['\"]") gemfile_regexes['source'] = re.compile( - r".*source(:|[ ]?=>)[ ]*['\"](?P[a-zA-Z:\/\.-\\]+)['\"].*") + r'source:[ ]?(?P[a-zA-Z:\/\.-]+)') gemfile_regexes['git'] = re.compile( - r".*git(:|[ ]?=>)[ ]*(?P[a-zA-Z:\/\.-]+).*") + r'git:[ ]?(?P[a-zA-Z:\/\.-]+)') gemfile_regexes['platform'] = re.compile( - r".*platform(:|[ ]?=>)[ ]*(?P[a-zA-Z:\/\.-]+).*") - gemfile_regexes['platforms'] = re.compile( - r".*platforms(:|[ ]?=>)[ ]*(?P\[.*\])[,]?.*") + r'platform:[ ]?(?P[a-zA-Z:\/\.-]+)') gemfile_regexes['path'] = re.compile( - r".*path(:|[ ]?=>)[ ]*(?P.+['\"\)]).*") - gemfile_regexes['github'] = re.compile( - r".*github(:|[ ]?=>)[ ]*[\'\"](?P[a-zA-Z:\/\.-0-9]+)[\'\"].*") + r'path:[ ]?(?P[a-zA-Z:\/\.-]+)') gemfile_regexes['branch'] = re.compile( - r".*branch(:|[ ]?=>)[ ]*(?P[a-zA-Z:\/\.-]+).*") + r'branch:[ ]?(?P[a-zA-Z:\/\.-]+)') gemfile_regexes['autorequire'] = re.compile( - r".*require(:|[ ]?=>)[ ]*(?P[a-zA-Z:\/\.-]+).*") + r'require:[ ]?(?P[a-zA-Z:\/\.-]+)') gemfile_regexes['group'] = re.compile( - r".*group(:|[ ]?=>)[ ]*(?P[a-zA-Z:\/\.-]+).*") - gemfile_regexes['groups'] = re.compile( - r".*groups(:|[ ]?=>)[ ]*(?P\[.*\]),.*") + r'group:[ ]?(?P[a-zA-Z:\/\.-]+)') + gemfile_regexes['name'] = re.compile( + r'(?P[a-zA-Z]+[\.0-9a-zA-Z _-]*)') gemfile_regexes['requirement'] = re.compile( - r"gem[ ]['\"].*?['\"](?P([>|<|=|~>|\d]+[ ]*[0-9\.\w]+[ ,]*)+).*") + r'(?P([>|<|=|~>|\d]+[ ]*[0-9\.\w]+[ ,]*)+)') global_group = 'runtime' group_block_regex = re.compile( - r".*group[ ]?(:|[ ]?=>)[ ]*(?P.*?) do") + r'group[ ]?:[ ]?(?P.*?) do') add_dvtdep_regex = re.compile( - r".*add_development_dependency (?P.*)") + r'.*add_development_dependency(?P.*)') add_rundep_regex = re.compile( - r".*add_runtime_dependency (?P.*)") + r'.*add_runtime_dependency(?P.*)') + add_dep_regex = re.compile( + r'.*dependency(?P.*)') def __init__(self, filepath, appname=''): self.filepath = filepath # Required when calls to gemspec occurs @@ -92,27 +80,33 @@ def __init__(self, filepath, appname=''): self.dependencies = { 'development': [], 'runtime': [], + 'dependency': [], 'test': [], 'production': [], 'metrics': [] } self.contents = self.gemfile.readlines() - if filepath.endswith('gemspec'): + path = ('gemspec', 'podspec') + if filepath.endswith(path): self.gemspec = True else: self.gemspec = False @staticmethod def preprocess(line): - '''Removes the comment portion and excess spaces.''' + """ + Return line after removing comment portion and excess spaces. + """ - if "#" in line: + if '#' in line: line = line[:line.index('#')] line = line.strip() return line def parse_line(self, line): - '''Parses each line and creates dependency objects accordingly''' + """ + Parses each line and creates dependency objects accordingly. + """ try: @@ -124,29 +118,43 @@ def parse_line(self, line): line = unicode(line) except NameError: pass - dep = self.Dependency() - dep.group = GemfileParser.global_group - if not self.appname: - dep.parent = [] - else: + linefile = io.StringIO(line) # csv requires a file object + for line in csv.reader(linefile, delimiter=','): + column_list = [] + for column in line: + stripped_column = column.replace("'", '') + stripped_column = stripped_column.replace('"', '') + stripped_column = stripped_column.replace('%q<', '') + stripped_column = stripped_column.replace('(', '') + stripped_column = stripped_column.replace(')', '') + stripped_column = stripped_column.replace('[', '') + stripped_column = stripped_column.replace(']', '') + stripped_column = stripped_column.strip() + column_list.append(stripped_column) + dep = self.Dependency() + dep.group = GemfileParser.global_group dep.parent.append(self.appname) - # Check for a match in each regex and assign to - # corresponding variables - for criteria in GemfileParser.gemfile_regexes: - criteria_regex = GemfileParser.gemfile_regexes[criteria] - match = criteria_regex.match(line) - if match: - if criteria == 'requirement': - dep.requirement.append(match.group(criteria)) - else: - setattr(dep, criteria, match.group(criteria)) - if dep.group in self.dependencies: - self.dependencies[dep.group].append(dep) - else: - self.dependencies[dep.group] = [dep] + for column in column_list: + # Check for a match in each regex and assign to + # corresponding variables + for criteria in GemfileParser.gemfile_regexes: + criteria_regex = GemfileParser.gemfile_regexes[criteria] + match = criteria_regex.match(column) + if match: + if criteria == 'requirement': + dep.requirement.append(match.group(criteria)) + else: + setattr(dep, criteria, match.group(criteria)) + break + if dep.group in self.dependencies: + self.dependencies[dep.group].append(dep) + else: + self.dependencies[dep.group] = [dep] def parse_gemfile(self, path=''): - '''Parses a Gemfile and returns a dict of categorized dependencies.''' + """ + Return dependencies after parsing gemfile. + """ if path == '': contents = self.contents @@ -165,22 +173,21 @@ def parse_gemfile(self, path=''): elif line.startswith('gemspec'): # Gemfile contains a call to gemspec gemfiledir = os.path.dirname(self.filepath) - gemspec_list = glob.glob(os.path.join(gemfiledir, "*.gemspec")) + gemspec_list = glob.glob(os.path.join(gemfiledir, '*.gemspec')) if len(gemspec_list) > 1: - print("Multiple gemspec files found") + print('Multiple gemspec files found') continue - elif len(gemspec_list) < 1: - print("No gemspec file found. Ignoring the gemspec call") - else: - gemspec_file = gemspec_list[0] - self.parse_gemspec( - path=os.path.join(gemfiledir, gemspec_file)) + gemspec_file = gemspec_list[0] + self.parse_gemspec(path=os.path.join(gemfiledir, gemspec_file)) elif line.startswith('gem '): + line = line[3:] self.parse_line(line) return self.dependencies def parse_gemspec(self, path=''): - '''Method to handle gemspec files.''' + """ + Return dependencies after parsing gemspec/podspec files. + """ if path == '': contents = self.contents @@ -195,15 +202,21 @@ def parse_gemspec(self, path=''): match = GemfileParser.add_rundep_regex.match(line) if match: GemfileParser.global_group = 'runtime' + else: + match = GemfileParser.add_dep_regex.match(line) + if match: + GemfileParser.global_group = 'dependency' if match: line = match.group('line') self.parse_line(line) return self.dependencies def parse(self): - '''Calls necessary function based on whether file is a gemspec file - or not and forwards the dicts returned by them.''' + """ + Calls necessary function based on whether file is a gemspec/podspec file + or not and forwards the dicts returned by them. + """ if self.gemspec: return self.parse_gemspec() else: - return self.parse_gemfile() + return self.parse_gemfile() \ No newline at end of file diff --git a/setup.py b/setup.py index db91450..cd3b618 100644 --- a/setup.py +++ b/setup.py @@ -5,24 +5,31 @@ from distutils.core import setup config = { - 'description': "Parse Ruby's Gemfiles", - 'author': 'Balasankar C', - 'url': 'https://gitlab.com/balasankarc/gemfileparser', - 'download_url': 'https://gitlab.com/balasankarc/gemfileparser', - 'author_email': 'balasankarc@autistici.org', + 'description': "A library to parse Rubygem gemspec and Gemfile files and Cocoapods podspec and Podfile files using Python. Friendly fork of https://gitlab.com/balasankarc/gemfileparser", + 'author': 'nexB', + 'url': 'https://github.com/nexB/gemfileparser2', + 'download_url': 'https://github.com/nexB/gemfileparser2', + 'author_email': 'info@aboutcode.org', 'version': '0.7.0', 'license': 'GPL-3+ and MIT', 'long_description': ''' +Introduction +~~~~~~~~~~~~ +Parse Ruby Gemfile's using Python. Supports Gemfiles, .gemspec and Cocoapods(.podspec) files. Friendly fork of https://gitlab.com/balasankarc/gemfileparser. + +https://gitlab.com/balasankarc/gemfileparser can only detect particular type of dependency in `.gemspec` files like it can detect only `s.add_development_dependency "rspec", "~>1.3.1"` or `s.add_runtime_dependency "rspec", "~>1.3.1"` type of dependency. Dependency should be in these 2 format only. +https://github.com/nexB/gemfileparser2 can detect all format of dependencies. This fork supports Gemfiles, .gemspec files and Cocoapods(.podspec) files. + Installation ~~~~~~~~~~~~ -| If using pip, use the command ``sudo pip install gemfileparser`` +| If using pip, use the command ``sudo pip install gemfileparser2`` | Else use the following commands :: - git clone https://github.com/balasankarc/gemfileparser.git - cd gemfileparser + git clone https://github.com/balasankarc/gemfileparser2.git + cd gemfileparser2 python setup.py install Usage @@ -30,7 +37,7 @@ :: - from gemfileparser import GemfileParser + from gemfileparser2 import GemfileParser parser = GemfileParser(, ) dependency_dictionary = parser.parse() @@ -61,7 +68,7 @@ :: - from gemfileparser import GemfileParser + from gemfileparser2 import GemfileParser n = GemfileParser('Gemfile', 'diaspora') deps = n.parse() for key in deps: @@ -82,10 +89,9 @@ .. _GNU GPL version 3 (or above) License: http://www.gnu.org/licenses/gpl ''', - 'install_requires': ['nose'], - 'packages': ['gemfileparser'], + 'packages': ['gemfileparser2'], 'scripts': [], - 'name': 'gemfileparser' + 'name': 'gemfileparser2' } setup(