Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup code and change to a more pythonic API #6

Open
wants to merge 17 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 65 additions & 36 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,75 +35,104 @@ ctags --fields=afmikKlnsStz readtags.c readtags.h
**Opening Tags File**
```python
import ctags
from ctags import CTags, TagEntry
from ctags import CTags
import sys

try:
tagFile = CTags('tags')
except:
except OSError as err:
print err
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to use Python-3-compatible syntax here.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "except OSError as err" is python3 syntax.
Do you speak about the print function ?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

On 17 May 2016 at 17:42, Matthieu Gautier [email protected] wrote:

In README.md
#6 (comment):

import sys

try:
tagFile = CTags('tags')
-except:
+except OSError as err:

  • print err

The "except OSError as err" is python3 syntax.
Do you speak about the print function ?


You are receiving this because you commented.
Reply to this email directly or view it on GitHub
https://github.com/jonashaag/python-ctags3/pull/6/files/540b4bfd9dda55f4b823652dc507d5cbe9c60621#r63547497

sys.exit(1)

# Available file information keys:
# opened - was the tag file successfully opened?
# error_number - errno value when 'opened' is false
# format - format of tag file (1 = original, 2 = extended)
# sort - how is the tag file sorted?
# author - name of author of generating program (may be empy string)
# name - name of program (may be empy string)
# url - URL of distribution (may be empy string)
# version - program version (may be empty string)

print tagFile['name']
print tagFile['author']
# sort - how is the tag file sorted?
#
# Other keys may be available:
# author - name of author of generating program
# name - name of program
# url - URL of distribution
# version - program version
# If one of them is not present a KeyError is raised.

try:
print tagFile['name']
except KeyError:
print "No 'name' in the tagfile"

try:
print tagFile['author']
except KeyError:
print "No 'author' in the tagfile"

print tagFile['format']

# Available sort type:
# TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED

# Note: use this only if you know how the tags file is sorted which is
# specified when you generate the tag file
status = tagFile.setSortType(ctags.TAG_SORTED)
tagFile.setSortType(ctags.TAG_SORTED)
```

**Obtaining First Tag Entry**
**Listing Tag Entries**
```python
entry = TagEntry()
status = tagFile.first(entry)

if status:
# Available TagEntry keys:
# name - name of tag
# file - path of source file containing definition of tag
# pattern - pattern for locating source line (None if no pattern)
# lineNumber - line number in source file of tag definition (may be zero if not known)
# kind - kind of tag (none if not known)
# fileScope - is tag of file-limited scope?

# Note: other keys will be assumed as an extension key and will
# return None if no such key is found

# A generator of all tags in the file can be obtain with:
all_tags = tagFile.all_tags()

# The generator yield a dict for each entry.
# The following keys are always available for a entry:
# name - name of tag
# file - path of source file containing definition of tag
# pattern - pattern for locating source line
# (None if no pattern, this should no huppen with a correct
# tag file)
# fileScope - is tag of file-limited scope?
#
# The dict may contain other keys (extension keys).
# Other keys include :
# lineNumber - line number in source file of tag definition
# kind - kind of tag

for entry in all_tags:
print entry['name']
print entry['kind']
print entry['file']
try:
entry['lineNumber']
except KeyError:
print "Entry has no lineNumber"
else:
print "Entry has a lineNumber"
```

**Finding a Tag Entry**
```python
**Finding Tag Entries**
```python
# Available options:
# TAG_PARTIALMATCH - begin with
# TAG_FULLMATCH - full length matching
# TAG_IGNORECASE - disable binary search
# TAG_OBSERVECASE - case sensitive and allowed binary search to perform

if tagFile.find(entry, 'find', ctags.TAG_PARTIALMATCH | ctags.TAG_IGNORECASE):
print 'found'
found_tags = tagFile.find_tags('find', ctags.TAG_PARTIALMATCH | ctags.TAG_IGNORECASE)
for entry in found_tags:
print entry['lineNumber']
print entry['pattern']
print entry['kind']

# Find the next tag matching the name and options supplied to the
# most recent call to tagFile.find(). (replace the entry if found)
status = tagFile.findNext(entry)
# most recent call to tagFile.find().
# Raise if no entry is found.
try:
entry = tagFile.findNext()
except:
...

# Step to the next tag in the file (replace entry if found)
status = tagFile.next(entry)
# Step to the next tag in the file.
# Raise if no entry is found.
try:
entry = tagFile.next()
except:
...
```
222 changes: 117 additions & 105 deletions src/_readtags.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -17,130 +17,142 @@ You should have received a copy of the GNU General Public License
along with Python-Ctags. If not, see <http://www.gnu.org/licenses/>.
"""

cdef extern from "string.h":
char* strerror(int errnum)

include "stdlib.pxi"
include "readtags.pxi"


cdef class TagEntry:
cdef tagEntry c_entry

def __cinit__(self):
self.c_entry.fields.count = 0
self.c_entry.fields.list = NULL


def __setitem__(self, key, item):
if key == 'name':
self.c_entry.name = item
elif key == 'file':
self.c_entry.file = item
elif key == 'pattern':
self.c_entry.address.pattern = item
elif key == 'lineNumber':
self.c_entry.address.lineNumber = item
elif key == 'kind':
self.c_entry.kind = item
elif key == 'fileScope':
self.c_entry.fileScope = item
elif key == 'fields':
# fields.list is allocated by readtags.c
if self.c_entry.fields.count != len(item):
return

fields = item
if self.c_entry.fields.list != NULL:
free(self.c_entry.fields.list)
self.c_entry.fields.list = NULL

for k, v in fields.iteritems():
self.c_entry.fields.list.key = k
self.c_entry.fields.list.value = v

def __getitem__(self, key):
cdef char* result
if key == 'name':
return self.c_entry.name
elif key == 'file':
return self.c_entry.file
elif key == 'pattern':
if self.c_entry.address.pattern == NULL:
return None
return self.c_entry.address.pattern
elif key == 'lineNumber':
return self.c_entry.address.lineNumber
elif key == 'kind':
if self.c_entry.kind == NULL:
return None
return self.c_entry.kind
elif key == 'fileScope':
return self.c_entry.fileScope
else:
# It will crash if we mix NULL/0/None
# don't mix comparison of type
result = ctagsField(&self.c_entry, key)
if result == NULL:
return None

return result
cdef create_tagEntry(const tagEntry* const c_entry):
cdef dict ret = {}
ret['name'] = c_entry.name
ret['file'] = c_entry.file
ret['fileScope'] = c_entry.fileScope
if c_entry.address.pattern != NULL:
ret['pattern'] = c_entry.address.pattern
if c_entry.address.lineNumber:
ret['lineNumber'] = c_entry.address.lineNumber
if c_entry.kind != NULL:
ret['kind'] = c_entry.kind
for index in range(c_entry.fields.count):
key = c_entry.fields.list[index].key
ret[key.decode()] = c_entry.fields.list[index].value
return ret

cdef class CTags:
cdef tagFile* file
cdef tagFileInfo info
cdef tagEntry c_entry
cdef object current_id

def __cinit__(self, filepath):
self.open(filepath)
self.file = ctagsOpen(filepath, &self.info)
if not self.file:
raise OSError(self.info.status.error_number,
strerror(self.info.status.error_number),
filepath)

def __dealloc__(self):

if self.file:
ctagsClose(self.file)

def __getitem__(self, key):
if key == 'opened':
return self.info.status.opened
if key == 'error_number':
return self.info.status.error_number
ret = None
if key == 'format':
return self.info.file.format
if key == 'sort':
elif key == 'sort':
return self.info.file.sort
if key == 'author':
if self.info.program.author == NULL:
return ''
return self.info.program.author
if key == 'name':
if self.info.program.name == NULL:
return ''
return self.info.program.name
if key == 'url':
if self.info.program.url == NULL:
return ''
return self.info.program.url
if key == 'version':
if self.info.program.version == NULL:
return ''
return self.info.program.version


def open(self, filepath):
self.file = ctagsOpen(filepath, &self.info)

if not self.info.status.opened:
raise Exception('Invalid tag file')
else:
if key == 'author':
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd do something like this to further clean up the code

if key in ('author', 'name', 'url', 'version'):
    ret = getattr(self.info.program, key)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure getattr works for c structure. Maybe cython is smart enough.
I will test.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, Cython is not smart enough :) getattr doesn't work on c structure.

ret = self.info.program.author
elif key == 'name':
ret = self.info.program.name
elif key == 'url':
ret = self.info.program.url
elif key == 'version':
ret = self.info.program.version
if ret is None:
raise KeyError(key)
return ret

def setSortType(self, tagSortType type):
return ctagsSetSortType(self.file, type)

def first(self, TagEntry entry):
return ctagsFirst(self.file, &entry.c_entry)

def find(self, TagEntry entry, char* name, int options):
return ctagsFind(self.file, &entry.c_entry, name, options)

def findNext(self, TagEntry entry):
return ctagsFindNext(self.file, &entry.c_entry)

def next(self, TagEntry entry):
return ctagsNext(self.file, &entry.c_entry)
success = ctagsSetSortType(self.file, type)
if not success:
raise RuntimeError()

cdef first(self):
success = ctagsFirst(self.file, &self.c_entry)
if not success:
raise RuntimeError()
return create_tagEntry(&self.c_entry)

cdef find(self, bytes name, int options):
success = ctagsFind(self.file, &self.c_entry, name, options)
if not success:
raise RuntimeError()
return create_tagEntry(&self.c_entry)

cdef findNext(self):
success = ctagsFindNext(self.file, &self.c_entry)
if not success:
raise RuntimeError()
return create_tagEntry(&self.c_entry)

cdef next(self):
success = ctagsNext(self.file, &self.c_entry)
if not success:
raise RuntimeError()
return create_tagEntry(&self.c_entry)

def find_tags(self, bytes name, int options):
""" Find tags corresponding to name in the tag file.
@name : a bytes array to search to.
@options : A option flags for the search.
@return : A iterator on all tags corresponding to the search.

WARNING: Only one iterator can run on a tag file.
If you use another iterator (by calling all_tags or find_tags),
any previous iterator will be invalidate and raise a RuntimeError.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

→ invalidated

"""
try:
first = self.find(name, options)
self.current_id = first
yield first
except KeyError:
raise StopIteration from None

while True:
if self.current_id is not first:
raise RuntimeError("Only one search/list generator at a time")
try:
other = self.findNext()
except RuntimeError:
raise StopIteration from None
else:
yield other

def all_tags(self):
""" List all tags in the tag file.
@return : A iterator on all tags in the file.

WARNING: Only one iterator can run on a tag file.
If you use another iterator (by calling all_tags or find_tags),
any previous iterator will be invalidate and raise a RuntimeError.
"""
try:
first = self.first()
self.current_id = first
yield first
except KeyError:
raise StopIteration from None

while True:
if self.current_id is not first:
raise RuntimeError("Only one search/list generator at a time")
try:
other = self.next()
except RuntimeError:
raise StopIteration from None
else:
yield other

4 changes: 2 additions & 2 deletions src/ctags/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@
"""


from ._readtags import TagEntry, CTags
__all__ = ['TagEntry', 'CTags']
from ._readtags import CTags
__all__ = ['CTags']

# sortType
TAG_UNSORTED=0
Expand Down
Loading