def stat(self, path):
"""Get attributes of a file or directory, following symlinks
This method queries the attributes of a file or directory.
If the path provided is a symbolic link, the returned
attributes should correspond to the target of the link.
:param bytes path:
The path of the remote file or directory to get attributes for
:returns: An :class:`SFTPAttrs` or an os.stat_result containing
the file attributes
:raises: :exc:`SFTPError` to return an error to the client
"""
return self.lstat(path)
python类stat_result()的实例源码
def listdir_stat(dirname):
"""Returns a list of tuples for each file contained in the named
directory, or None if the directory does not exist. Each tuple
contains the filename, followed by the tuple returned by
calling os.stat on the filename.
"""
import os
def stat(filename):
rstat = os.stat(filename)
if IS_UPY:
# Micropython dates are relative to Jan 1, 2000. On the host, time
# is relative to Jan 1, 1970.
return rstat[:7] + tuple(tim + TIME_OFFSET for tim in rstat[7:])
return tuple(rstat) # PGH formerly returned an os.stat_result instance
try:
files = os.listdir(dirname)
except OSError:
return None
if dirname == '/':
return list((file, stat('/' + file)) for file in files)
return list((file, stat(dirname + '/' + file)) for file in files)
def test_stat_bounds(monkeypatch):
def mockstat(path):
monkeypatch.undo()
result = os.stat(path)
return os.stat_result((
result.st_mode,
2 ** 32 + result.st_ino + 1, # Ensure st_ino > 2 ** 32
result.st_dev,
result.st_nlink,
result.st_uid,
result.st_gid,
result.st_size,
result.st_atime,
result.st_mtime,
result.st_ctime,
))
monkeypatch.setattr(os, 'stat', mockstat)
path = get_data_path('stat')
result = stat(path)
for key, value in result.items():
assert value < 2 ** 32, 'expected {} to have value less than 2 ** 32'.format(key)
def _make_details_from_stat(cls, stat_result):
"""Make a *details* info dict from an `os.stat_result` object.
"""
details = {
'_write': ['accessed', 'modified'],
'accessed': stat_result.st_atime,
'modified': stat_result.st_mtime,
'size': stat_result.st_size,
'type': int(cls._get_type_from_stat(stat_result))
}
# On other Unix systems (such as FreeBSD), the following
# attributes may be available (but may be only filled out if
# root tries to use them):
details['created'] = getattr(stat_result, 'st_birthtime', None)
ctime_key = (
'created'
if _WINDOWS_PLATFORM
else 'metadata_changed'
)
details[ctime_key] = stat_result.st_ctime
return details
def _make_access_from_stat(cls, stat_result):
"""Make an *access* info dict from an `os.stat_result` object.
"""
access = {}
access['permissions'] = Permissions(
mode=stat_result.st_mode
).dump()
access['gid'] = stat_result.st_gid
access['uid'] = stat_result.st_uid
if not _WINDOWS_PLATFORM:
import grp
import pwd
try:
access['group'] = grp.getgrgid(access['gid']).gr_name
except KeyError: # pragma: nocover
pass
try:
access['user'] = pwd.getpwuid(access['uid']).pw_name
except KeyError: # pragma: nocover
pass
return access
def test_oldSingleDigitDayOfMonth(self):
"""
A file with a high-resolution timestamp which falls on a day of the
month which can be represented by one decimal digit is formatted with
one padding 0 to preserve the columns which come after it.
"""
# A point about 7 months in the past, tweaked to fall on the first of a
# month so we test the case we want to test.
then = self.now - (60 * 60 * 24 * 31 * 7) + (60 * 60 * 24 * 5)
stat = os.stat_result((0, 0, 0, 0, 0, 0, 0, 0, then, 0))
self.assertEqual(
self._lsInTimezone('America/New_York', stat),
'!--------- 0 0 0 0 May 01 1973 foo')
self.assertEqual(
self._lsInTimezone('Pacific/Auckland', stat),
'!--------- 0 0 0 0 May 02 1973 foo')
def test_localeIndependent(self):
"""
The month name in the date is locale independent.
"""
# A point about three months in the past.
then = self.now - (60 * 60 * 24 * 31 * 3)
stat = os.stat_result((0, 0, 0, 0, 0, 0, 0, 0, then, 0))
# Fake that we're in a language where August is not Aug (e.g.: Spanish)
currentLocale = locale.getlocale()
locale.setlocale(locale.LC_ALL, "es_AR.UTF8")
self.addCleanup(locale.setlocale, locale.LC_ALL, currentLocale)
self.assertEqual(
self._lsInTimezone('America/New_York', stat),
'!--------- 0 0 0 0 Aug 28 17:33 foo')
self.assertEqual(
self._lsInTimezone('Pacific/Auckland', stat),
'!--------- 0 0 0 0 Aug 29 09:33 foo')
# If alternate locale is not available, the previous test will be
# skipped, please install this locale for it to run
def test_newSingleDigitDayOfMonth(self):
"""
A file with a high-resolution timestamp which falls on a day of the
month which can be represented by one decimal digit is formatted with
one padding 0 to preserve the columns which come after it.
"""
# A point about three months in the past, tweaked to fall on the first
# of a month so we test the case we want to test.
then = self.now - (60 * 60 * 24 * 31 * 3) + (60 * 60 * 24 * 4)
stat = os.stat_result((0, 0, 0, 0, 0, 0, 0, 0, then, 0))
self.assertEqual(
self._lsInTimezone('America/New_York', stat),
'!--------- 0 0 0 0 Sep 01 17:33 foo')
self.assertEqual(
self._lsInTimezone('Pacific/Auckland', stat),
'!--------- 0 0 0 0 Sep 02 09:33 foo')
def getStat(self):
try:
return os.stat(self)
except:
# return a null stat_result object
return os.stat_result([0 for n in range(os.stat_result.n_sequence_fields)])
def lstat(self, path):
"""Get attributes of a file, directory, or symlink
This method queries the attributes of a file, directory,
or symlink. Unlike :meth:`stat`, this method should
return the attributes of a symlink itself rather than
the target of that link.
:param bytes path:
The path of the file, directory, or link to get attributes for
:returns: An :class:`SFTPAttrs` or an os.stat_result containing
the file attributes
:raises: :exc:`SFTPError` to return an error to the client
"""
try:
stat = self._vfs.stat(path).stat
except VFSFileNotFoundError:
raise SFTPError(FX_NO_SUCH_FILE, 'No such file')
mod = S_IFDIR if stat.type == Stat.DIRECTORY else S_IFREG
return SFTPAttrs(**{
'size': stat.size,
'uid': os.getuid(),
'gid': os.getgid(),
'permissions': mod | S_IRWXU | S_IRWXG | S_IRWXO,
'atime': stat.atime,
'mtime': stat.mtime
})
def fstat(self, file_obj):
"""Get attributes of an open file
:param file file_obj:
The file to get attributes for
:returns: An :class:`SFTPAttrs` or an os.stat_result containing
the file attributes
:raises: :exc:`SFTPError` to return an error to the client
"""
return self.lstat(file_obj.path)
def _get_type_from_stat(cls, _stat):
"""Get the resource type from an `os.stat_result` object.
"""
st_mode = _stat.st_mode
st_type = stat.S_IFMT(st_mode)
return cls.STAT_TO_RESOURCE_TYPE.get(st_type, ResourceType.unknown)
# --------------------------------------------------------
# Required Methods
# --------------------------------------------------------
def _scandir(self, path, namespaces=None):
self.check()
namespaces = namespaces or ()
_path = self.validatepath(path)
sys_path = self._to_sys_path(_path)
with convert_os_errors('scandir', path, directory=True):
for dir_entry in scandir(sys_path):
info = {
"basic": {
"name": dir_entry.name,
"is_dir": dir_entry.is_dir()
}
}
if 'details' in namespaces:
stat_result = dir_entry.stat()
info['details'] = \
self._make_details_from_stat(stat_result)
if 'stat' in namespaces:
stat_result = dir_entry.stat()
info['stat'] = {
k: getattr(stat_result, k)
for k in dir(stat_result) if k.startswith('st_')
}
if 'lstat' in namespaces:
lstat_result = dir_entry.stat(follow_symlinks=False)
info['lstat'] = {
k: getattr(lstat_result, k)
for k in dir(lstat_result) if k.startswith('st_')
}
if 'link' in namespaces:
info['link'] = self._make_link_info(
os.path.join(sys_path, dir_entry.name)
)
if 'access' in namespaces:
stat_result = dir_entry.stat()
info['access'] =\
self._make_access_from_stat(stat_result)
yield Info(info)
def _scandir(self, path, namespaces=None):
self.check()
namespaces = namespaces or ()
_path = self.validatepath(path)
sys_path = self._to_sys_path(_path)
with convert_os_errors('scandir', path, directory=True):
for entry_name in os.listdir(sys_path):
entry_path = os.path.join(sys_path, entry_name)
stat_result = os.stat(entry_path)
info = {
"basic": {
"name": entry_name,
"is_dir": stat.S_ISDIR(stat_result.st_mode),
}
}
if 'details' in namespaces:
info['details'] = \
self._make_details_from_stat(stat_result)
if 'stat' in namespaces:
info['stat'] = {
k: getattr(stat_result, k)
for k in dir(stat_result) if k.startswith('st_')
}
if 'lstat' in namespaces:
lstat_result = os.lstat(entry_path)
info['lstat'] = {
k: getattr(lstat_result, k)
for k in dir(lstat_result) if k.startswith('st_')
}
if 'link' in namespaces:
info['link'] = self._make_link_info(
os.path.join(sys_path, entry_name)
)
if 'access' in namespaces:
info['access'] =\
self._make_access_from_stat(stat_result)
yield Info(info)
def test_stat_result_pickle(self):
result = os.stat(self.fname)
for proto in range(pickle.HIGHEST_PROTOCOL + 1):
p = pickle.dumps(result, proto)
self.assertIn(b'stat_result', p)
if proto < 4:
self.assertIn(b'cos\nstat_result\n', p)
unpickled = pickle.loads(p)
self.assertEqual(result, unpickled)
def _stat(self, real, path, *args, **kwargs):
info = list(real(self._real_path(path)))
if path in self._ownership:
info[4:5] = self._ownership[path]
return os.stat_result(info)
def test_oldFile(self):
"""
A file with an mtime six months (approximately) or more in the past has
a listing including a low-resolution timestamp.
"""
# Go with 7 months. That's more than 6 months.
then = self.now - (60 * 60 * 24 * 31 * 7)
stat = os.stat_result((0, 0, 0, 0, 0, 0, 0, 0, then, 0))
self.assertEqual(
self._lsInTimezone('America/New_York', stat),
'!--------- 0 0 0 0 Apr 26 1973 foo')
self.assertEqual(
self._lsInTimezone('Pacific/Auckland', stat),
'!--------- 0 0 0 0 Apr 27 1973 foo')
def test_newFile(self):
"""
A file with an mtime fewer than six months (approximately) in the past
has a listing including a high-resolution timestamp excluding the year.
"""
# A point about three months in the past.
then = self.now - (60 * 60 * 24 * 31 * 3)
stat = os.stat_result((0, 0, 0, 0, 0, 0, 0, 0, then, 0))
self.assertEqual(
self._lsInTimezone('America/New_York', stat),
'!--------- 0 0 0 0 Aug 28 17:33 foo')
self.assertEqual(
self._lsInTimezone('Pacific/Auckland', stat),
'!--------- 0 0 0 0 Aug 29 09:33 foo')
def find_syntax_node_name(evaluator, python_object):
try:
path = inspect.getsourcefile(python_object)
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)
return None, None
if path is None or not os.path.exists(path):
# The path might not exist or be e.g. <stdin>.
return None, None
module = _load_module(evaluator, path, python_object)
if inspect.ismodule(python_object):
# We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``).
return module, path
try:
name_str = python_object.__name__
except AttributeError:
# Stuff like python_function.__code__.
return None, None
if name_str == '<lambda>':
return None, None # It's too hard to find lambdas.
# Doesn't always work (e.g. os.stat_result)
try:
names = module.used_names[name_str]
except KeyError:
return None, None
names = [n for n in names if n.is_definition()]
try:
code = python_object.__code__
# By using the line number of a code object we make the lookup in a
# file pretty easy. There's still a possibility of people defining
# stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people
# do so we just don't care.
line_nr = code.co_firstlineno
except AttributeError:
pass
else:
line_names = [name for name in names if name.start_pos[0] == line_nr]
# There's a chance that the object is not available anymore, because
# the code has changed in the background.
if line_names:
return line_names[-1].parent, path
# It's really hard to actually get the right definition, here as a last
# resort we just return the last one. This chance might lead to odd
# completions at some points but will lead to mostly correct type
# inference, because people tend to define a public name in a module only
# once.
return names[-1].parent, path
def check_stat_attributes(self, fname):
if not hasattr(os, "stat"):
return
import stat
result = os.stat(fname)
# Make sure direct access works
self.assertEqual(result[stat.ST_SIZE], 3)
self.assertEqual(result.st_size, 3)
# Make sure all the attributes are there
members = dir(result)
for name in dir(stat):
if name[:3] == 'ST_':
attr = name.lower()
if name.endswith("TIME"):
def trunc(x): return int(x)
else:
def trunc(x): return x
self.assertEqual(trunc(getattr(result, attr)),
result[getattr(stat, name)])
self.assertIn(attr, members)
try:
result[200]
self.fail("No exception thrown")
except IndexError:
pass
# Make sure that assignment fails
try:
result.st_mode = 1
self.fail("No exception thrown")
except AttributeError:
pass
try:
result.st_rdev = 1
self.fail("No exception thrown")
except (AttributeError, TypeError):
pass
try:
result.parrot = 1
self.fail("No exception thrown")
except AttributeError:
pass
# Use the stat_result constructor with a too-short tuple.
try:
result2 = os.stat_result((10,))
self.fail("No exception thrown")
except TypeError:
pass
# Use the constructor with a too-long tuple.
try:
result2 = os.stat_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def test_stat_attributes(self):
import stat
result = os.stat(self.fname)
# Make sure direct access works
self.assertEqual(result[stat.ST_SIZE], 3)
self.assertEqual(result.st_size, 3)
# Make sure all the attributes are there
members = dir(result)
for name in dir(stat):
if name[:3] == 'ST_':
attr = name.lower()
if name.endswith("TIME"):
def trunc(x): return int(x)
else:
def trunc(x): return x
self.assertEqual(trunc(getattr(result, attr)),
result[getattr(stat, name)])
self.assertIn(attr, members)
try:
result[200]
self.fail("No exception raised")
except IndexError:
pass
# Make sure that assignment fails
try:
result.st_mode = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.st_rdev = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.parrot = 1
self.fail("No exception raised")
except AttributeError:
pass
# Use the stat_result constructor with a too-short tuple.
try:
result2 = os.stat_result((10,))
self.fail("No exception raised")
except TypeError:
pass
# Use the constructor with a too-long tuple.
try:
result2 = os.stat_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def test_stat_attributes(self):
import stat
result = os.stat(self.fname)
# Make sure direct access works
self.assertEqual(result[stat.ST_SIZE], 3)
self.assertEqual(result.st_size, 3)
# Make sure all the attributes are there
members = dir(result)
for name in dir(stat):
if name[:3] == 'ST_':
attr = name.lower()
if name.endswith("TIME"):
def trunc(x): return int(x)
else:
def trunc(x): return x
self.assertEqual(trunc(getattr(result, attr)),
result[getattr(stat, name)])
self.assertIn(attr, members)
try:
result[200]
self.fail("No exception raised")
except IndexError:
pass
# Make sure that assignment fails
try:
result.st_mode = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.st_rdev = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.parrot = 1
self.fail("No exception raised")
except AttributeError:
pass
# Use the stat_result constructor with a too-short tuple.
try:
result2 = os.stat_result((10,))
self.fail("No exception raised")
except TypeError:
pass
# Use the constructor with a too-long tuple.
try:
result2 = os.stat_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def test_stat_attributes(self):
import stat
result = os.stat(self.fname)
# Make sure direct access works
self.assertEqual(result[stat.ST_SIZE], 3)
self.assertEqual(result.st_size, 3)
# Make sure all the attributes are there
members = dir(result)
for name in dir(stat):
if name[:3] == 'ST_':
attr = name.lower()
if name.endswith("TIME"):
def trunc(x): return int(x)
else:
def trunc(x): return x
self.assertEqual(trunc(getattr(result, attr)),
result[getattr(stat, name)])
self.assertIn(attr, members)
try:
result[200]
self.fail("No exception raised")
except IndexError:
pass
# Make sure that assignment fails
try:
result.st_mode = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.st_rdev = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.parrot = 1
self.fail("No exception raised")
except AttributeError:
pass
# Use the stat_result constructor with a too-short tuple.
try:
result2 = os.stat_result((10,))
self.fail("No exception raised")
except TypeError:
pass
# Use the constructor with a too-long tuple.
try:
result2 = os.stat_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def test_stat_attributes(self):
if not hasattr(os, "stat"):
return
import stat
result = os.stat(self.fname)
# Make sure direct access works
self.assertEqual(result[stat.ST_SIZE], 3)
self.assertEqual(result.st_size, 3)
# Make sure all the attributes are there
members = dir(result)
for name in dir(stat):
if name[:3] == 'ST_':
attr = name.lower()
if name.endswith("TIME"):
def trunc(x): return int(x)
else:
def trunc(x): return x
self.assertEqual(trunc(getattr(result, attr)),
result[getattr(stat, name)])
self.assertIn(attr, members)
try:
result[200]
self.fail("No exception raised")
except IndexError:
pass
# Make sure that assignment fails
try:
result.st_mode = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.st_rdev = 1
self.fail("No exception raised")
except (AttributeError, TypeError):
pass
try:
result.parrot = 1
self.fail("No exception raised")
except AttributeError:
pass
# Use the stat_result constructor with a too-short tuple.
try:
result2 = os.stat_result((10,))
self.fail("No exception raised")
except TypeError:
pass
# Use the constructor with a too-long tuple.
try:
result2 = os.stat_result((0,1,2,3,4,5,6,7,8,9,10,11,12,13,14))
except TypeError:
pass
def find_syntax_node_name(evaluator, python_object):
try:
path = inspect.getsourcefile(python_object)
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)
return None, None
if path is None or not os.path.exists(path):
# The path might not exist or be e.g. <stdin>.
return None, None
module = _load_module(evaluator, path, python_object)
if inspect.ismodule(python_object):
# We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``).
return module, path
try:
name_str = python_object.__name__
except AttributeError:
# Stuff like python_function.__code__.
return None, None
if name_str == '<lambda>':
return None, None # It's too hard to find lambdas.
# Doesn't always work (e.g. os.stat_result)
try:
names = module.used_names[name_str]
except KeyError:
return None, None
names = [n for n in names if n.is_definition()]
try:
code = python_object.__code__
# By using the line number of a code object we make the lookup in a
# file pretty easy. There's still a possibility of people defining
# stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people
# do so we just don't care.
line_nr = code.co_firstlineno
except AttributeError:
pass
else:
line_names = [name for name in names if name.start_pos[0] == line_nr]
# There's a chance that the object is not available anymore, because
# the code has changed in the background.
if line_names:
return line_names[-1].parent, path
# It's really hard to actually get the right definition, here as a last
# resort we just return the last one. This chance might lead to odd
# completions at some points but will lead to mostly correct type
# inference, because people tend to define a public name in a module only
# once.
return names[-1].parent, path
def scan_paths(
self, rel=True, files=True, symlinks=True, dirs=True, exclude=None,
memoize=True, lookup=True) -> Dict[str, os.stat_result]:
"""Get the paths and stats of files in the directory.
Symlinks are not followed. Directory paths and their os.stat_result
objects are cached so that the filesystem is not scanned each time the
method is called.
Args:
rel: Return relative file paths.
files: Include regular files.
symlinks: Include symbolic links.
dirs: Include directories.
exclude: An iterable of relative paths of files to not include in
the output.
memoize: If true, use cached data. Otherwise, re-scan the
filesystem.
lookup: Return a defaultdict that looks up the stats of files not
already in the dictionary.
Returns:
A dict with file paths as keys and stat objects as values.
"""
exclude = set() if exclude is None else set(exclude)
if lookup:
def lookup_stat(path: str) -> os.stat_result:
full_path = os.path.join(self.path, path)
for entry, rel_path in self._sub_entries:
if entry.path == full_path:
return entry.stat()
return os.stat(full_path, follow_symlinks=False)
output = FactoryDict(lookup_stat)
else:
output = {}
if not memoize or not self._sub_entries:
self._sub_entries = []
for entry in scan_tree(self.path):
# Computing the relative path is expensive to do each time.
rel_path = os.path.relpath(entry.path, self.path)
self._sub_entries.append((entry, rel_path))
for entry, rel_path in self._sub_entries:
if entry.is_file(follow_symlinks=False) and not files:
continue
elif entry.is_dir(follow_symlinks=False) and not dirs:
continue
elif entry.is_symlink() and not symlinks:
continue
elif rel_path in exclude:
continue
else:
if rel:
output[rel_path] = entry.stat(follow_symlinks=False)
else:
output[entry.path] = entry.stat(follow_symlinks=False)
return output
def find_syntax_node_name(evaluator, python_object):
try:
path = inspect.getsourcefile(python_object)
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)
return None, None
if path is None or not os.path.exists(path):
# The path might not exist or be e.g. <stdin>.
return None, None
module = _load_module(evaluator, path, python_object)
if inspect.ismodule(python_object):
# We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``).
return module, path
try:
name_str = python_object.__name__
except AttributeError:
# Stuff like python_function.__code__.
return None, None
if name_str == '<lambda>':
return None, None # It's too hard to find lambdas.
# Doesn't always work (e.g. os.stat_result)
try:
names = module.used_names[name_str]
except KeyError:
return None, None
names = [n for n in names if n.is_definition()]
try:
code = python_object.__code__
# By using the line number of a code object we make the lookup in a
# file pretty easy. There's still a possibility of people defining
# stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people
# do so we just don't care.
line_nr = code.co_firstlineno
except AttributeError:
pass
else:
line_names = [name for name in names if name.start_pos[0] == line_nr]
# There's a chance that the object is not available anymore, because
# the code has changed in the background.
if line_names:
return line_names[-1].parent, path
# It's really hard to actually get the right definition, here as a last
# resort we just return the last one. This chance might lead to odd
# completions at some points but will lead to mostly correct type
# inference, because people tend to define a public name in a module only
# once.
return names[-1].parent, path
def find_syntax_node_name(evaluator, python_object):
try:
python_object = _get_object_to_check(python_object)
path = inspect.getsourcefile(python_object)
except TypeError:
# The type might not be known (e.g. class_with_dict.__weakref__)
return None, None
if path is None or not os.path.exists(path):
# The path might not exist or be e.g. <stdin>.
return None, None
module = _load_module(evaluator, path, python_object)
if inspect.ismodule(python_object):
# We don't need to check names for modules, because there's not really
# a way to write a module in a module in Python (and also __name__ can
# be something like ``email.utils``).
return module, path
try:
name_str = python_object.__name__
except AttributeError:
# Stuff like python_function.__code__.
return None, None
if name_str == '<lambda>':
return None, None # It's too hard to find lambdas.
# Doesn't always work (e.g. os.stat_result)
try:
names = module.get_used_names()[name_str]
except KeyError:
return None, None
names = [n for n in names if n.is_definition()]
try:
code = python_object.__code__
# By using the line number of a code object we make the lookup in a
# file pretty easy. There's still a possibility of people defining
# stuff like ``a = 3; foo(a); a = 4`` on the same line, but if people
# do so we just don't care.
line_nr = code.co_firstlineno
except AttributeError:
pass
else:
line_names = [name for name in names if name.start_pos[0] == line_nr]
# There's a chance that the object is not available anymore, because
# the code has changed in the background.
if line_names:
return line_names[-1].parent, path
# It's really hard to actually get the right definition, here as a last
# resort we just return the last one. This chance might lead to odd
# completions at some points but will lead to mostly correct type
# inference, because people tend to define a public name in a module only
# once.
return names[-1].parent, path