66from ruamel .yaml import YAML
77
88from ..base import BaseImage
9+ from ...utils import is_local_pip_requirement
910
1011# pattern for parsing conda dependency line
1112PYTHON_REGEX = re .compile (r"python\s*=+\s*([\d\.]*)" )
@@ -127,6 +128,50 @@ def get_build_script_files(self):
127128 files .update (super ().get_build_script_files ())
128129 return files
129130
131+ _environment_yaml = None
132+
133+ @property
134+ def environment_yaml (self ):
135+ if self ._environment_yaml is not None :
136+ return self ._environment_yaml
137+
138+ environment_yml = self .binder_path ("environment.yml" )
139+ if not os .path .exists (environment_yml ):
140+ self ._environment_yaml = {}
141+ return self ._environment_yaml
142+
143+ with open (environment_yml ) as f :
144+ env = YAML ().load (f )
145+ # check if the env file is empty, if so instantiate an empty dictionary.
146+ if env is None :
147+ env = {}
148+ # check if the env file provided a dick-like thing not a list or other data structure.
149+ if not isinstance (env , Mapping ):
150+ raise TypeError (
151+ "environment.yml should contain a dictionary. Got %r" % type (env )
152+ )
153+ self ._environment_yaml = env
154+
155+ return self ._environment_yaml
156+
157+ @property
158+ def _should_preassemble_env (self ):
159+ """Check for local pip requirements in environment.yaml
160+
161+ If there are any local references, e.g. `-e .`,
162+ stage the whole repo prior to installation.
163+ """
164+ dependencies = self .environment_yaml .get ("dependencies" , [])
165+ pip_requirements = None
166+ for dep in dependencies :
167+ if isinstance (dep , dict ) and dep .get ("pip" ):
168+ pip_requirements = dep ["pip" ]
169+ if isinstance (pip_requirements , list ):
170+ for line in pip_requirements :
171+ if is_local_pip_requirement (line ):
172+ return False
173+ return True
174+
130175 @property
131176 def python_version (self ):
132177 """Detect the Python version for a given `environment.yml`
@@ -135,31 +180,17 @@ def python_version(self):
135180 or a Falsy empty string '' if not found.
136181
137182 """
138- environment_yml = self .binder_path ("environment.yml" )
139- if not os .path .exists (environment_yml ):
140- return ""
141-
142183 if not hasattr (self , "_python_version" ):
143184 py_version = None
144- with open (environment_yml ) as f :
145- env = YAML ().load (f )
146- # check if the env file is empty, if so instantiate an empty dictionary.
147- if env is None :
148- env = {}
149- # check if the env file provided a dick-like thing not a list or other data structure.
150- if not isinstance (env , Mapping ):
151- raise TypeError (
152- "environment.yml should contain a dictionary. Got %r"
153- % type (env )
154- )
155- for dep in env .get ("dependencies" , []):
156- if not isinstance (dep , str ):
157- continue
158- match = PYTHON_REGEX .match (dep )
159- if not match :
160- continue
161- py_version = match .group (1 )
162- break
185+ env = self .environment_yaml
186+ for dep in env .get ("dependencies" , []):
187+ if not isinstance (dep , str ):
188+ continue
189+ match = PYTHON_REGEX .match (dep )
190+ if not match :
191+ continue
192+ py_version = match .group (1 )
193+ break
163194
164195 # extract major.minor
165196 if py_version :
@@ -185,19 +216,20 @@ def get_preassemble_script_files(self):
185216 repo contents change
186217 """
187218 assemble_files = super ().get_preassemble_script_files ()
188- environment_yml = self .binder_path ("environment.yml" )
189- if os .path .exists (environment_yml ):
190- assemble_files [environment_yml ] = environment_yml
219+ if self ._should_preassemble_env :
220+ environment_yml = self .binder_path ("environment.yml" )
221+ if os .path .exists (environment_yml ):
222+ assemble_files [environment_yml ] = environment_yml
191223 return assemble_files
192224
193- def get_preassemble_scripts (self ):
225+ def get_env_scripts (self ):
194226 """Return series of build-steps specific to this source repository.
195227 """
196- assembly_scripts = []
228+ scripts = []
197229 environment_yml = self .binder_path ("environment.yml" )
198230 env_prefix = "${KERNEL_PYTHON_PREFIX}" if self .py2 else "${NB_PYTHON_PREFIX}"
199231 if os .path .exists (environment_yml ):
200- assembly_scripts .append (
232+ scripts .append (
201233 (
202234 "${NB_USER}" ,
203235 r"""
@@ -209,7 +241,19 @@ def get_preassemble_scripts(self):
209241 ),
210242 )
211243 )
212- return super ().get_preassemble_scripts () + assembly_scripts
244+ return scripts
245+
246+ def get_preassemble_scripts (self ):
247+ scripts = super ().get_preassemble_scripts ()
248+ if self ._should_preassemble_env :
249+ scripts .extend (self .get_env_scripts ())
250+ return scripts
251+
252+ def get_assemble_scripts (self ):
253+ scripts = super ().get_assemble_scripts ()
254+ if not self ._should_preassemble_env :
255+ scripts .extend (self .get_env_scripts ())
256+ return scripts
213257
214258 def detect (self ):
215259 """Check if current repo should be built with the Conda BuildPack.
0 commit comments