Coverage for sources/emcdproj/__/distribution.py: 95%
34 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-09 03:38 +0000
« prev ^ index » next coverage.py v7.8.0, created at 2025-04-09 03:38 +0000
1# vim: set filetype=python fileencoding=utf-8:
2# -*- coding: utf-8 -*-
4#============================================================================#
5# #
6# Licensed under the Apache License, Version 2.0 (the "License"); #
7# you may not use this file except in compliance with the License. #
8# You may obtain a copy of the License at #
9# #
10# http://www.apache.org/licenses/LICENSE-2.0 #
11# #
12# Unless required by applicable law or agreed to in writing, software #
13# distributed under the License is distributed on an "AS IS" BASIS, #
14# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. #
15# See the License for the specific language governing permissions and #
16# limitations under the License. #
17# #
18#============================================================================#
21''' Information about package distribution. '''
24from . import imports as __
25# from . import io as _io
28class Information( metaclass = __.ImmutableDataclass ):
29 ''' Information about a package distribution. '''
31 name: str
32 location: __.Path
33 editable: bool
35 @classmethod
36 async def prepare(
37 selfclass, package: str, exits: __.ctxl.AsyncExitStack,
38 project_anchor: __.Absential[ __.Path ] = __.absent,
39 ) -> __.typx.Self:
40 ''' Acquires information about our package distribution. '''
41 import sys
42 # Detect PyInstaller bundle.
43 if getattr( sys, 'frozen', False ) and hasattr( sys, '_MEIPASS' ): 43 ↛ 44line 43 didn't jump to line 44 because the condition on line 43 was never true
44 project_anchor = __.Path( getattr( sys, '_MEIPASS' ) )
45 # TODO: Python 3.12: importlib.metadata
46 from importlib_metadata import packages_distributions
47 # https://github.com/pypa/packaging-problems/issues/609
48 name = packages_distributions( ).get( package )
49 if name is None: # Development sources rather than distribution.
50 editable = True # Implies no use of importlib.resources.
51 location, name = (
52 await _acquire_development_information(
53 location = project_anchor ) )
54 else:
55 editable = False
56 name = name[ 0 ]
57 location = await _acquire_production_location( package, exits )
58 return selfclass(
59 editable = editable, location = location, name = name )
61 def provide_data_location( self, *appendages: str ) -> __.Path:
62 ''' Provides location of distribution data. '''
63 base = self.location / 'data'
64 if appendages: return base.joinpath( *appendages )
65 return base
68async def _acquire_development_information(
69 location: __.Absential[ __.Path ] = __.absent
70) -> tuple[ __.Path, str ]:
71 from tomli import loads
72 if __.is_absent( location ):
73 location = __.Path( __file__ ).parents[ 3 ].resolve( strict = True )
74 # pyproject = await _io.acquire_text_file_async(
75 # location / 'pyproject.toml', deserializer = loads )
76 with ( location / 'pyproject.toml' ).open( ) as file:
77 pyproject = loads( file.read( ) )
78 name = pyproject[ 'project' ][ 'name' ]
79 return location, name
82async def _acquire_production_location(
83 package: str, exits: __.ctxl.AsyncExitStack
84) -> __.Path:
85 # TODO: Python 3.12: importlib.resources
86 # TODO: 'importlib_resources' PR to fix 'as_file' type signature.
87 from importlib_resources import files, as_file # pyright: ignore
88 # Extract package contents to temporary directory, if necessary.
89 return exits.enter_context(
90 as_file( files( package ) ) ) # pyright: ignore