In [1]:
from conda_tools import (cache, environment)
from conda_tools import environment_utils as eu
from conda_tools import cache_utils as cu

import os
from os.path import join
from itertools import groupby, chain
from versio.version import Version

# adjust root to be your Miniconda prefix
root = r"C:\Users\Ryan\Miniconda3"
root_envs = join(root, 'envs')
root_pkgs = join(root, 'pkgs')
print(root_envs)
print(root_pkgs)


C:\Users\Ryan\Miniconda3\envs
C:\Users\Ryan\Miniconda3\pkgs

The two core components of the conda ecosystem are the package cache and the environment subfolders. These are abstracted with PackageInfo and Environment objects respectively.

Here we create "pools" of PackageInfo and Environment objects. These objects permit easy, read-only access to various bits of metadata stored in the package cache and conda-meta/ subfolders in the environment. We want to reuse the objects as much as we can to minimize disk I/O. All the disk reads are currently cached with the objects, so the more objects you work with, the more RAM will be required.


In [2]:
# Create pkg_cache and environments
pkg_cache = cache.packages(root_pkgs)
envs = environment.environments(root_envs)
print(pkg_cache[:5])
print()
print(envs[:5])


(PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\alabaster-0.7.8-py35_0) @ 0x13dfd352828, PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\anaconda-client-1.4.0-py35_0) @ 0x13dfd3527b8, PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\babel-2.3.3-py35_0) @ 0x13dfd352860, PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\backports-1.0-py27_0) @ 0x13dfd3526d8, PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\backports-1.0-py35_0) @ 0x13dfd3527f0)

(Environment(C:\Users\Ryan\Miniconda3) @ 0x13dfd39aeb8, Environment(C:\Users\Ryan\Miniconda3\envs\adamimage) @ 0x13dfd39afd0, Environment(C:\Users\Ryan\Miniconda3\envs\ct_test1) @ 0x13dfd389080, Environment(C:\Users\Ryan\Miniconda3\envs\ct_test2) @ 0x13dfd389128, Environment(C:\Users\Ryan\Miniconda3\envs\ct_test3) @ 0x13dfd389198)

Packages

Conda packages all have an info/ subdirectory for storing metadata about the package. PackageInfo provide convenient access to this metadata.


In [3]:
pi = pkg_cache[0]
pi.index  # info/index.json


Out[3]:
{'arch': 'x86_64',
 'build': 'py35_0',
 'build_number': 0,
 'depends': ['python 3.5*'],
 'license': 'BSD',
 'name': 'alabaster',
 'platform': 'win',
 'subdir': 'win-64',
 'version': '0.7.8'}

In [4]:
# We can access fields of index.json directly from the object.
pi.name, pi.version, pi.build


Out[4]:
('alabaster', '0.7.8', 'py35_0')

In [5]:
# Access to info/files
pi.files


Out[5]:
frozenset({'Lib/site-packages/alabaster-0.7.8-py3.5.egg-info/PKG-INFO',
           'Lib/site-packages/alabaster-0.7.8-py3.5.egg-info/SOURCES.txt',
           'Lib/site-packages/alabaster-0.7.8-py3.5.egg-info/dependency_links.txt',
           'Lib/site-packages/alabaster-0.7.8-py3.5.egg-info/pbr.json',
           'Lib/site-packages/alabaster-0.7.8-py3.5.egg-info/top_level.txt',
           'Lib/site-packages/alabaster/__init__.py',
           'Lib/site-packages/alabaster/__pycache__/__init__.cpython-35.pyc',
           'Lib/site-packages/alabaster/__pycache__/_version.cpython-35.pyc',
           'Lib/site-packages/alabaster/__pycache__/support.cpython-35.pyc',
           'Lib/site-packages/alabaster/_version.py',
           'Lib/site-packages/alabaster/about.html',
           'Lib/site-packages/alabaster/donate.html',
           'Lib/site-packages/alabaster/layout.html',
           'Lib/site-packages/alabaster/navigation.html',
           'Lib/site-packages/alabaster/relations.html',
           'Lib/site-packages/alabaster/static/alabaster.css_t',
           'Lib/site-packages/alabaster/static/custom.css',
           'Lib/site-packages/alabaster/support.py',
           'Lib/site-packages/alabaster/theme.conf'})

In [6]:
# The full spec of the package.  This is always "name-version-build"
pi.full_spec


Out[6]:
'alabaster-0.7.8-py35_0'

In [7]:
# We can queries against the information we have on packages
# For example, I want to find all MIT licensed packages in the cache
{pi.full_spec: pi.license for pi in pkg_cache if pi.license == 'MIT'}


Out[7]:
{'entrypoints-0.2-py35_1': 'MIT',
 'entrypoints-0.2.2-py35_0': 'MIT',
 'get_terminal_size-1.0.0-py27_0': 'MIT',
 'get_terminal_size-1.0.0-py35_0': 'MIT',
 'imagesize-0.7.1-py35_0': 'MIT',
 'jsonschema-2.5.1-py35_0': 'MIT',
 'nodejs-4.4.1-0': 'MIT',
 'path.py-8.2.1-py27_0': 'MIT',
 'path.py-8.2.1-py35_0': 'MIT',
 'pathlib2-2.1.0-py27_0': 'MIT',
 'pickleshare-0.7.2-py27_0': 'MIT',
 'pickleshare-0.7.2-py35_0': 'MIT',
 'pip-8.1.1-py35_1': 'MIT',
 'pip-8.1.2-py27_0': 'MIT',
 'pip-8.1.2-py34_0': 'MIT',
 'pip-8.1.2-py35_0': 'MIT',
 'py-1.4.31-py35_0': 'MIT',
 'pyparsing-2.1.4-py35_0': 'MIT',
 'pytest-2.9.2-py35_0': 'MIT',
 'pytz-2016.4-py35_0': 'MIT',
 'ruamel_yaml-0.11.7-py35_0': 'MIT',
 'simplejson-3.8.2-py35_0': 'MIT',
 'six-1.10.0-py27_0': 'MIT',
 'six-1.10.0-py35_0': 'MIT',
 'six-1.9.0-py35_0': 'MIT',
 'wcwidth-0.1.7-py35_0': 'MIT',
 'wheel-0.29.0-py27_0': 'MIT',
 'wheel-0.29.0-py34_0': 'MIT',
 'wheel-0.29.0-py35_0': 'MIT',
 'win_unicode_console-0.5-py35_0': 'MIT'}

Environments


In [8]:
e = envs[2]
e


Out[8]:
Environment(C:\Users\Ryan\Miniconda3\envs\ct_test1) @ 0x13dfd389080

In [9]:
# We can discover the currently activated environment
{e.path: e.activated() for e in envs}


Out[9]:
{'C:\\Users\\Ryan\\Miniconda3': True,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\_build': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\adamimage': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\ct_test1': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\ct_test2': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\ct_test3': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\env1': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\env2': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\ftest': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\imagep': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\jupyterlab-dev': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\m1': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\old1': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\pipct': False,
 'C:\\Users\\Ryan\\Miniconda3\\envs\\py27': False}

In [10]:
# We can see all the packages that claim to be linked into the environment, keyed by name
e.linked_packages


Out[10]:
{'pip': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\pip-8.1.2-py35_0) @ 0x13dfd389860,
 'python': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\python-3.5.1-5) @ 0x13dfd389b70,
 'setuptools': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\setuptools-23.0.0-py35_0) @ 0x13dfd389c88,
 'ujson': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\ujson-1.35-py35_0) @ 0x13dfd389ba8,
 'vs2015_runtime': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\vs2015_runtime-14.0.25123-0) @ 0x13dfd3db5f8,
 'wget': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\wget-2.2-py35_0) @ 0x13dfd389940,
 'wheel': PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\wheel-0.29.0-py35_0) @ 0x13dfd46b7f0}

In [11]:
# linked packages are either hard-linked, symlinked, or copied into environments.
set(chain(e.hard_linked, e.soft_linked, e.copy_linked)) ^ set(e.linked_packages.values())


Out[11]:
set()

In [12]:
# The origin channel of each package
e.package_channels


Out[12]:
{'pip': 'https://repo.continuum.io/pkgs/free/win-64',
 'python': 'https://repo.continuum.io/pkgs/free/win-64',
 'setuptools': 'https://repo.continuum.io/pkgs/free/win-64',
 'ujson': 'https://repo.continuum.io/pkgs/free/win-64',
 'vs2015_runtime': 'https://repo.continuum.io/pkgs/free/win-64',
 'wget': 'https://repo.continuum.io/pkgs/free/win-64',
 'wheel': 'https://repo.continuum.io/pkgs/free/win-64'}

In [13]:
# We also have access to the history of the environment.
# The history object is an adaptation of conda's history parser.
# (note: The interface to this may change in the future)
e.history.object_log


Out[13]:
[{'date': '2016-07-01 20:52:32',
  'downgrade': [],
  'install': ['pip-8.1.2-py35_0',
   'python-3.5.1-5',
   'setuptools-23.0.0-py35_0',
   'ujson-1.35-py35_0',
   'vs2015_runtime-14.0.25123-0',
   'wget-2.2-py35_0',
   'wheel-0.29.0-py35_0'],
  'remove': [],
  'rev': 0,
  'upgrade': []}]

Neat stuff

Convenient access to the package cache and environment metadata allows you to do some neat stuff relatively easily.

Below are a few examples of some quick ideas that can be implemented with little effort.


In [14]:
# Calculate potential collisions in environments by packages claiming the same file paths
# Very quick and naive way of detecting file path collisions.
for i, p1 in enumerate(pkg_cache):
    for p2 in pkg_cache[i+1:]:
        if p1.name == p2.name:
            continue
        x = p1.files.intersection(p2.files)
        if x:
            print("{} collides with {}".format(p1, p2))
            print("\tCollisions: ", x)


hdf4-4.2.11-0 collides with hdf5-1.8.15.1-2
	Collisions:  frozenset({'Library/COPYING', 'Library/RELEASE.txt'})
notebook-4.2.0-py35_0 collides with qtconsole-4.2.1-py35_0
	Collisions:  frozenset({'Menu/jupyter.ico'})
notebook-4.2.1-py35_0 collides with qtconsole-4.2.1-py35_0
	Collisions:  frozenset({'Menu/jupyter.ico'})

In [15]:
# Cache Utils has some higher order, convenience functions

# See what environments a package is linked into
# Note that this is a O(n) operation where n is the sum of the installed packages in each environment you're checking.
# If you're running this for the first time, it has to read all the metadata for each environment.
# Also note, that this creates new package info objects and environment objects each run, so each run
# prompts a full scan of both the package cache and all environments.
cu.linked_environments((pkg_cache[0],), envs)


Out[15]:
{PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\alabaster-0.7.8-py35_0) @ 0x13dfd352828: (Environment(C:\Users\Ryan\Miniconda3) @ 0x13dfd39aeb8,)}

In [16]:
# Find which environments the latest packages are linked to.
# This example uses Versio to parse and compare PEP440 compliant version numbers
# This will exclude packages like packages like jpeg and openssl

# This loop simple creates Version objects so we can compare them later.
Versions = {}
for x in pkg_cache:
    try:
        if x.name in Versions:
            Versions[x.name].append(Version(x.version))
        else:
            Versions[x.name] = [Version(x.version)]
    except:
        print("Skipping ", x.name, x.version)
    
# sort the value lists and pick the latest versions
#pversions = {k: str(list(sorted(v))[-1]) for k, v in Versions.items()}

# sort the value lists and pick the older versions
pversions = {k: list(map(str, list(sorted(v))[:-1])) for k, v in Versions.items()}

# The most up-to-date packages are linked to which environments?
#latest_pkgs = [x for x in pkg_cache if x.name in pversions and x.version == pversions[x.name]]

# Find the environments that older packages are linked to
latest_pkgs = [x for x in pkg_cache if x.name in pversions and x.version in set(pversions[x.name])]

# Simply print the results nicely
{str(k): list(map(str, v)) for k, v in cu.linked_environments(latest_pkgs, envs).items()}


Skipping  jpeg 8d
Skipping  jpeg 8d
Skipping  openssl 1.0.2h
Out[16]:
{'backports-1.0-py27_0': ['Environment: py27'],
 'backports-1.0-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'conda-4.0.7-py35_0': [],
 'conda-4.0.8-py35_0': [],
 'conda-4.1.2-py35_0': [],
 'conda-4.1.6-py35_0': [],
 'conda-build-1.20.3-py35_0': [],
 'conda-build-1.21.5-py35_0': [],
 'conda-env-2.5.0-py35_0': [],
 'conda-env-2.5.1-py35_0': [],
 'decorator-4.0.10-py27_0': ['Environment: py27'],
 'decorator-4.0.10-py35_0': ['Environment: env1',
  'Environment: ftest',
  'Environment: jupyterlab-dev'],
 'decorator-4.0.9-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env2',
  'Environment: imagep'],
 'entrypoints-0.2-py35_1': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: imagep'],
 'get_terminal_size-1.0.0-py27_0': ['Environment: py27'],
 'get_terminal_size-1.0.0-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'ipython-4.2.0-py27_0': ['Environment: py27'],
 'ipython-4.2.0-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep'],
 'ipython_genutils-0.1.0-py27_0': ['Environment: py27'],
 'ipython_genutils-0.1.0-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'jupyter_client-4.2.2-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: imagep'],
 'notebook-4.2.0-py35_0': ['Environment: Miniconda3'],
 'numpy-1.10.4-py35_2': ['Environment: old1'],
 'numpy-1.11.0-py35_1': ['Environment: adamimage',
  'Environment: ftest',
  'Environment: imagep'],
 'numpy-1.11.1-py27_0': ['Environment: py27'],
 'numpy-1.11.1-py35_0': ['Environment: env1'],
 'path.py-8.2.1-py27_0': ['Environment: py27'],
 'path.py-8.2.1-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'pickleshare-0.7.2-py27_0': ['Environment: py27'],
 'pickleshare-0.7.2-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'pillow-3.2.0-py35_0': ['Environment: adamimage', 'Environment: imagep'],
 'pillow-3.2.0-py35_1': ['Environment: env1'],
 'pip-8.1.1-py35_1': ['Environment: env1', 'Environment: env2'],
 'pip-8.1.2-py27_0': ['Environment: pipct',
  'Environment: py27',
  'Environment: _build'],
 'pip-8.1.2-py34_0': [],
 'pip-8.1.2-py35_0': ['Environment: adamimage',
  'Environment: ct_test1',
  'Environment: ct_test2',
  'Environment: ct_test3',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev',
  'Environment: m1',
  'Environment: old1'],
 'pyreadline-2.1-py27_0': ['Environment: py27'],
 'pyreadline-2.1-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'python-2.7.12-0': ['Environment: pipct',
  'Environment: py27',
  'Environment: _build'],
 'python-3.4.5-0': [],
 'python-3.5.1-4': ['Environment: adamimage',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep'],
 'python-3.5.1-5': ['Environment: Miniconda3',
  'Environment: ct_test1',
  'Environment: ct_test2',
  'Environment: ct_test3',
  'Environment: env1',
  'Environment: old1'],
 'scikit-image-0.12.3-np111py35_0': ['Environment: adamimage',
  'Environment: imagep'],
 'scikit-image-0.12.3-np111py35_1': ['Environment: env1'],
 'scipy-0.17.1-np111py27_1': ['Environment: py27'],
 'scipy-0.17.1-np111py35_0': ['Environment: adamimage',
  'Environment: env1',
  'Environment: ftest',
  'Environment: imagep'],
 'setuptools-21.2.1-py35_0': ['Environment: env1'],
 'setuptools-22.0.5-py35_0': ['Environment: adamimage',
  'Environment: env2',
  'Environment: imagep'],
 'setuptools-23.0.0-py27_0': ['Environment: pipct',
  'Environment: py27',
  'Environment: _build'],
 'setuptools-23.0.0-py34_0': [],
 'setuptools-23.0.0-py35_0': ['Environment: ct_test1',
  'Environment: ct_test2',
  'Environment: ct_test3',
  'Environment: ftest',
  'Environment: jupyterlab-dev',
  'Environment: m1',
  'Environment: old1'],
 'simplegeneric-0.8.1-py27_1': ['Environment: py27'],
 'simplegeneric-0.8.1-py35_1': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'six-1.10.0-py27_0': ['Environment: py27'],
 'six-1.10.0-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'six-1.9.0-py35_0': ['Environment: m1'],
 'traitlets-4.2.1-py27_0': ['Environment: py27'],
 'traitlets-4.2.1-py35_0': ['Environment: Miniconda3',
  'Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev'],
 'vs2015_runtime-14.00.23026.0-0': ['Environment: adamimage',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep'],
 'wheel-0.29.0-py27_0': ['Environment: pipct',
  'Environment: py27',
  'Environment: _build'],
 'wheel-0.29.0-py34_0': [],
 'wheel-0.29.0-py35_0': ['Environment: adamimage',
  'Environment: ct_test1',
  'Environment: ct_test2',
  'Environment: ct_test3',
  'Environment: env1',
  'Environment: env2',
  'Environment: ftest',
  'Environment: imagep',
  'Environment: jupyterlab-dev',
  'Environment: m1',
  'Environment: old1'],
 'zlib-1.2.8-0': [],
 'zlib-1.2.8-vc14_3': ['Environment: adamimage',
  'Environment: env1',
  'Environment: imagep']}

In [17]:
# All packages that are not linked to any environment
cu.unlinked_packages(pkg_cache, envs)


Out[17]:
(PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-4.0.7-py35_0) @ 0x13dfd352908,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\hdf5-1.8.15.1-2) @ 0x13dfd3815f8,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\pip-8.1.2-py34_0) @ 0x13dfd39a048,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-4.1.6-py35_0) @ 0x13dfd371860,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-4.1.2-py35_0) @ 0x13dfd371c88,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\xlwt-1.1.2-py35_0) @ 0x13dfd39acc0,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\python-3.4.5-0) @ 0x13dfd39a2e8,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-build-1.21.5-py35_0) @ 0x13dfd37d6a0,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-env-2.5.0-py35_0) @ 0x13dfd37d860,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\jpeg-8d-0) @ 0x13dfd381828,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\wheel-0.29.0-py34_0) @ 0x13dfd39ac18,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-build-1.20.3-py35_0) @ 0x13dfd37d400,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\hdf4-4.2.11-0) @ 0x13dfd3815c0,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\vs2010_runtime-10.00.40219.1-2) @ 0x13dfd39aac8,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-4.0.8-py35_0) @ 0x13dfd352978,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\setuptools-23.0.0-py34_0) @ 0x13dfd39a6d8,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\conda-env-2.5.1-py35_0) @ 0x13dfd37d898,
 PackageInfo(C:\Users\Ryan\Miniconda3\pkgs\zlib-1.2.8-0) @ 0x13dfd39acf8)

In [18]:
# Environment representation of root environment
e = environment.Environment(join(root_envs, 'env2'))

In [19]:
# Long running.  Disk intensive.
filter_pyc = lambda f: filter(lambda x: not x.endswith('.pyc'), f)

In [20]:
# List all files in an environment that are not hardlinked (and should be).
# Note that *.pyc files are filtered out.
not_linked = {x: tuple(filter_pyc(y)) for x, y in eu.check_hardlinked_env(envs[0]).items()}

# If you wish to see all the non-existant hardlinks, including *.pyc files, remove the filter_pyc function call
# not_linked = {x: y for x, y in eu.check_hardlinked_env(envs[0]).items()}

not_linked


Out[20]:
{'alabaster': (),
 'anaconda-client': (),
 'babel': (),
 'backports': (),
 'clyent': (),
 'colorama': (),
 'conda': (),
 'conda-build': (),
 'conda-env': (),
 'decorator': (),
 'docutils': ('Scripts/rst2pseudoxml.py',
  'Scripts/rst2odt.py',
  'Scripts/rst2latex.py',
  'Scripts/rstpep2html.py',
  'Scripts/rst2s5.py',
  'Scripts/rst2odt_prepstyles.py',
  'Scripts/rst2xml.py',
  'Scripts/rst2man.py',
  'Scripts/rst2html.py',
  'Scripts/rst2xetex.py'),
 'entrypoints': (),
 'get_terminal_size': (),
 'imagesize': (),
 'ipykernel': (),
 'ipython': (),
 'ipython_genutils': (),
 'jinja2': (),
 'jsonschema': (),
 'jupyter_client': (),
 'jupyter_core': (),
 'markupsafe': (),
 'mistune': (),
 'nbconvert': (),
 'nbformat': (),
 'nodejs': (),
 'notebook': (),
 'patch': (),
 'path.py': (),
 'pickleshare': (),
 'psutil': (),
 'py': (),
 'pygments': (),
 'pyreadline': (),
 'pytest': (),
 'python': ('python.exe', 'DLLs/_ctypes.pyd', 'python35.dll'),
 'python-dateutil': (),
 'pytz': (),
 'pyzmq': (),
 'ruamel_yaml': (),
 'semantic_version': (),
 'setuptools': (),
 'simplegeneric': (),
 'six': (),
 'snowballstemmer': (),
 'sphinx': (),
 'sphinx_rtd_theme': (),
 'tornado': (),
 'traitlets': ()}

In [21]:
# We can leverage the information in the environment's history to get packages 
# that were explicitly installed by the user.
eu.explicitly_installed(e)


Out[21]:
{'2016-06-06 16:45:23': {'ipython-4.2.0-py35_0', 'python-3.5.1-4'}}

In [ ]: