Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
cpython
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
cpython
Commits
6a600aba
Commit
6a600aba
authored
Feb 10, 2003
by
Jack Jansen
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added docstrings.
parent
72df65ac
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
130 additions
and
6 deletions
+130
-6
Lib/plat-mac/pimp.py
Lib/plat-mac/pimp.py
+130
-6
No files found.
Lib/plat-mac/pimp.py
View file @
6a600aba
"""Package Install Manager for Python.
This is currently a MacOSX-only strawman implementation.
Motto: "He may be shabby, but he gets you what you need" :-)
Tools to allow easy installation of packages. The idea is that there is
an online XML database per (platform, python-version) containing packages
known to work with that combination. This module contains tools for getting
and parsing the database, testing whether packages are installed, computing
dependencies and installing packages.
There is a minimal main program that works as a command line tool, but the
intention is that the end user will use this through a GUI.
"""
import
sys
import
sys
import
os
import
os
import
urllib
import
urllib
...
@@ -6,6 +20,8 @@ import plistlib
...
@@ -6,6 +20,8 @@ import plistlib
import
distutils.util
import
distutils.util
import
md5
import
md5
__all__
=
[
"PimpPreferences"
,
"PimpDatabase"
,
"PimpPackage"
,
"main"
]
_scriptExc_NotInstalled
=
"pimp._scriptExc_NotInstalled"
_scriptExc_NotInstalled
=
"pimp._scriptExc_NotInstalled"
_scriptExc_OldInstalled
=
"pimp._scriptExc_OldInstalled"
_scriptExc_OldInstalled
=
"pimp._scriptExc_OldInstalled"
_scriptExc_BadInstalled
=
"pimp._scriptExc_BadInstalled"
_scriptExc_BadInstalled
=
"pimp._scriptExc_BadInstalled"
...
@@ -32,6 +48,9 @@ class MyURLopener(urllib.FancyURLopener):
...
@@ -32,6 +48,9 @@ class MyURLopener(urllib.FancyURLopener):
urllib
.
URLopener
.
http_error_default
(
self
,
url
,
fp
,
errcode
,
errmsg
,
headers
)
urllib
.
URLopener
.
http_error_default
(
self
,
url
,
fp
,
errcode
,
errmsg
,
headers
)
class
PimpPreferences
:
class
PimpPreferences
:
"""Container for per-user preferences, such as the database to use
and where to install packages"""
def
__init__
(
self
,
def
__init__
(
self
,
flavorOrder
=
None
,
flavorOrder
=
None
,
downloadDir
=
None
,
downloadDir
=
None
,
...
@@ -55,6 +74,9 @@ class PimpPreferences:
...
@@ -55,6 +74,9 @@ class PimpPreferences:
self
.
pimpDatabase
=
pimpDatabase
self
.
pimpDatabase
=
pimpDatabase
def
check
(
self
):
def
check
(
self
):
"""Check that the preferences make sense: directories exist and are
writable, the install directory is on sys.path, etc."""
rv
=
""
rv
=
""
RWX_OK
=
os
.
R_OK
|
os
.
W_OK
|
os
.
X_OK
RWX_OK
=
os
.
R_OK
|
os
.
W_OK
|
os
.
X_OK
if
not
os
.
path
.
exists
(
self
.
downloadDir
):
if
not
os
.
path
.
exists
(
self
.
downloadDir
):
...
@@ -83,6 +105,8 @@ class PimpPreferences:
...
@@ -83,6 +105,8 @@ class PimpPreferences:
return
rv
return
rv
def
compareFlavors
(
self
,
left
,
right
):
def
compareFlavors
(
self
,
left
,
right
):
"""Compare two flavor strings. This is part of your preferences
because whether the user prefers installing from source or binary is."""
if
left
in
self
.
flavorOrder
:
if
left
in
self
.
flavorOrder
:
if
right
in
self
.
flavorOrder
:
if
right
in
self
.
flavorOrder
:
return
cmp
(
self
.
flavorOrder
.
index
(
left
),
self
.
flavorOrder
.
index
(
right
))
return
cmp
(
self
.
flavorOrder
.
index
(
left
),
self
.
flavorOrder
.
index
(
right
))
...
@@ -92,6 +116,11 @@ class PimpPreferences:
...
@@ -92,6 +116,11 @@ class PimpPreferences:
return
cmp
(
left
,
right
)
return
cmp
(
left
,
right
)
class
PimpDatabase
:
class
PimpDatabase
:
"""Class representing a pimp database. It can actually contain
information from multiple databases through inclusion, but the
toplevel database is considered the master, as its maintainer is
"responsible" for the contents"""
def
__init__
(
self
,
prefs
):
def
__init__
(
self
,
prefs
):
self
.
_packages
=
[]
self
.
_packages
=
[]
self
.
preferences
=
prefs
self
.
preferences
=
prefs
...
@@ -101,6 +130,10 @@ class PimpDatabase:
...
@@ -101,6 +130,10 @@ class PimpDatabase:
self
.
_description
=
""
self
.
_description
=
""
def
appendURL
(
self
,
url
,
included
=
0
):
def
appendURL
(
self
,
url
,
included
=
0
):
"""Append packages from the database with the given URL.
Only the first database should specify included=0, so the
global information (maintainer, description) get stored."""
if
url
in
self
.
_urllist
:
if
url
in
self
.
_urllist
:
return
return
self
.
_urllist
.
append
(
url
)
self
.
_urllist
.
append
(
url
)
...
@@ -111,26 +144,39 @@ class PimpDatabase:
...
@@ -111,26 +144,39 @@ class PimpDatabase:
self
.
_version
=
dict
.
get
(
'version'
,
'0.1'
)
self
.
_version
=
dict
.
get
(
'version'
,
'0.1'
)
self
.
_maintainer
=
dict
.
get
(
'maintainer'
,
''
)
self
.
_maintainer
=
dict
.
get
(
'maintainer'
,
''
)
self
.
_description
=
dict
.
get
(
'description'
,
''
)
self
.
_description
=
dict
.
get
(
'description'
,
''
)
self
.
appendPackages
(
dict
[
'packages'
])
self
.
_
appendPackages
(
dict
[
'packages'
])
others
=
dict
.
get
(
'include'
,
[])
others
=
dict
.
get
(
'include'
,
[])
for
url
in
others
:
for
url
in
others
:
self
.
appendURL
(
url
,
included
=
1
)
self
.
appendURL
(
url
,
included
=
1
)
def
appendPackages
(
self
,
packages
):
def
_appendPackages
(
self
,
packages
):
"""Given a list of dictionaries containing package
descriptions create the PimpPackage objects and append them
to our internal storage."""
for
p
in
packages
:
for
p
in
packages
:
pkg
=
PimpPackage
(
self
,
**
dict
(
p
))
pkg
=
PimpPackage
(
self
,
**
dict
(
p
))
self
.
_packages
.
append
(
pkg
)
self
.
_packages
.
append
(
pkg
)
def
list
(
self
):
def
list
(
self
):
"""Return a list of all PimpPackage objects in the database."""
return
self
.
_packages
return
self
.
_packages
def
listnames
(
self
):
def
listnames
(
self
):
"""Return a list of names of all packages in the database."""
rv
=
[]
rv
=
[]
for
pkg
in
self
.
_packages
:
for
pkg
in
self
.
_packages
:
rv
.
append
(
_fmtpackagename
(
pkg
))
rv
.
append
(
_fmtpackagename
(
pkg
))
return
rv
return
rv
def
dump
(
self
,
pathOrFile
):
def
dump
(
self
,
pathOrFile
):
"""Dump the contents of the database to an XML .plist file.
The file can be passed as either a file object or a pathname.
All data, including included databases, is dumped."""
packages
=
[]
packages
=
[]
for
pkg
in
self
.
_packages
:
for
pkg
in
self
.
_packages
:
packages
.
append
(
pkg
.
dump
())
packages
.
append
(
pkg
.
dump
())
...
@@ -144,6 +190,13 @@ class PimpDatabase:
...
@@ -144,6 +190,13 @@ class PimpDatabase:
plist
.
write
(
pathOrFile
)
plist
.
write
(
pathOrFile
)
def
find
(
self
,
ident
):
def
find
(
self
,
ident
):
"""Find a package. The package can be specified by name
or as a dictionary with name, version and flavor entries.
Only name is obligatory. If there are multiple matches the
best one (higher version number, flavors ordered according to
users' preference) is returned."""
if
type
(
ident
)
==
str
:
if
type
(
ident
)
==
str
:
# Remove ( and ) for pseudo-packages
# Remove ( and ) for pseudo-packages
if
ident
[
0
]
==
'('
and
ident
[
-
1
]
==
')'
:
if
ident
[
0
]
==
'('
and
ident
[
-
1
]
==
')'
:
...
@@ -175,6 +228,8 @@ class PimpDatabase:
...
@@ -175,6 +228,8 @@ class PimpDatabase:
return
found
return
found
class
PimpPackage
:
class
PimpPackage
:
"""Class representing a single package."""
def
__init__
(
self
,
db
,
name
,
def
__init__
(
self
,
db
,
name
,
version
=
None
,
version
=
None
,
flavor
=
None
,
flavor
=
None
,
...
@@ -200,6 +255,7 @@ class PimpPackage:
...
@@ -200,6 +255,7 @@ class PimpPackage:
self
.
_MD5Sum
=
MD5Sum
self
.
_MD5Sum
=
MD5Sum
def
dump
(
self
):
def
dump
(
self
):
"""Return a dict object containing the information on the package."""
dict
=
{
dict
=
{
'name'
:
self
.
name
,
'name'
:
self
.
name
,
}
}
...
@@ -226,15 +282,24 @@ class PimpPackage:
...
@@ -226,15 +282,24 @@ class PimpPackage:
return
dict
return
dict
def
__cmp__
(
self
,
other
):
def
__cmp__
(
self
,
other
):
"""Compare two packages, where the "better" package sorts lower."""
if
not
isinstance
(
other
,
PimpPackage
):
if
not
isinstance
(
other
,
PimpPackage
):
return
cmp
(
id
(
self
),
id
(
other
))
return
cmp
(
id
(
self
),
id
(
other
))
if
self
.
name
!=
other
.
name
:
if
self
.
name
!=
other
.
name
:
return
cmp
(
self
.
name
,
other
.
name
)
return
cmp
(
self
.
name
,
other
.
name
)
if
self
.
version
!=
other
.
version
:
if
self
.
version
!=
other
.
version
:
return
cmp
(
self
.
version
,
other
.
version
)
return
-
cmp
(
self
.
version
,
other
.
version
)
return
self
.
_db
.
preferences
.
compareFlavors
(
self
.
flavor
,
other
.
flavor
)
return
self
.
_db
.
preferences
.
compareFlavors
(
self
.
flavor
,
other
.
flavor
)
def
installed
(
self
):
def
installed
(
self
):
"""Test wheter the package is installed.
Returns two values: a status indicator which is one of
"yes", "no", "old" (an older version is installed) or "bad"
(something went wrong during the install test) and a human
readable string which may contain more details."""
namespace
=
{
namespace
=
{
"NotInstalled"
:
_scriptExc_NotInstalled
,
"NotInstalled"
:
_scriptExc_NotInstalled
,
"OldInstalled"
:
_scriptExc_OldInstalled
,
"OldInstalled"
:
_scriptExc_OldInstalled
,
...
@@ -259,6 +324,14 @@ class PimpPackage:
...
@@ -259,6 +324,14 @@ class PimpPackage:
return
"yes"
,
""
return
"yes"
,
""
def
prerequisites
(
self
):
def
prerequisites
(
self
):
"""Return a list of prerequisites for this package.
The list contains 2-tuples, of which the first item is either
a PimpPackage object or None, and the second is a descriptive
string. The first item can be None if this package depends on
something that isn't pimp-installable, in which case the descriptive
string should tell the user what to do."""
rv
=
[]
rv
=
[]
if
not
self
.
downloadURL
:
if
not
self
.
downloadURL
:
return
[(
None
,
"This package needs to be installed manually"
)]
return
[(
None
,
"This package needs to be installed manually"
)]
...
@@ -278,6 +351,8 @@ class PimpPackage:
...
@@ -278,6 +351,8 @@ class PimpPackage:
return
rv
return
rv
def
_cmd
(
self
,
output
,
dir
,
*
cmditems
):
def
_cmd
(
self
,
output
,
dir
,
*
cmditems
):
"""Internal routine to run a shell command in a given directory."""
cmd
=
(
"cd
\
"
%s
\
"
; "
%
dir
)
+
" "
.
join
(
cmditems
)
cmd
=
(
"cd
\
"
%s
\
"
; "
%
dir
)
+
" "
.
join
(
cmditems
)
if
output
:
if
output
:
output
.
write
(
"+ %s
\
n
"
%
cmd
)
output
.
write
(
"+ %s
\
n
"
%
cmd
)
...
@@ -293,7 +368,18 @@ class PimpPackage:
...
@@ -293,7 +368,18 @@ class PimpPackage:
rv
=
fp
.
close
()
rv
=
fp
.
close
()
return
rv
return
rv
def
downloadSinglePackage
(
self
,
output
):
def
downloadSinglePackage
(
self
,
output
=
None
):
"""Download a single package, if needed.
An MD5 signature is used to determine whether download is needed,
and to test that we actually downloaded what we expected.
If output is given it is a file-like object that will receive a log
of what happens.
If anything unforeseen happened the method returns an error message
string.
"""
scheme
,
loc
,
path
,
query
,
frag
=
urlparse
.
urlsplit
(
self
.
downloadURL
)
scheme
,
loc
,
path
,
query
,
frag
=
urlparse
.
urlsplit
(
self
.
downloadURL
)
path
=
urllib
.
url2pathname
(
path
)
path
=
urllib
.
url2pathname
(
path
)
filename
=
os
.
path
.
split
(
path
)[
1
]
filename
=
os
.
path
.
split
(
path
)[
1
]
...
@@ -312,6 +398,8 @@ class PimpPackage:
...
@@ -312,6 +398,8 @@ class PimpPackage:
return
"archive does not have correct MD5 checksum"
return
"archive does not have correct MD5 checksum"
def
_archiveOK
(
self
):
def
_archiveOK
(
self
):
"""Test an archive. It should exist and the MD5 checksum should be correct."""
if
not
os
.
path
.
exists
(
self
.
archiveFilename
):
if
not
os
.
path
.
exists
(
self
.
archiveFilename
):
return
0
return
0
if
not
self
.
_MD5Sum
:
if
not
self
.
_MD5Sum
:
...
@@ -321,7 +409,9 @@ class PimpPackage:
...
@@ -321,7 +409,9 @@ class PimpPackage:
checksum
=
md5
.
new
(
data
).
hexdigest
()
checksum
=
md5
.
new
(
data
).
hexdigest
()
return
checksum
==
self
.
_MD5Sum
return
checksum
==
self
.
_MD5Sum
def
unpackSinglePackage
(
self
,
output
):
def
unpackSinglePackage
(
self
,
output
=
None
):
"""Unpack a downloaded package archive."""
filename
=
os
.
path
.
split
(
self
.
archiveFilename
)[
1
]
filename
=
os
.
path
.
split
(
self
.
archiveFilename
)[
1
]
for
ext
,
cmd
in
ARCHIVE_FORMATS
:
for
ext
,
cmd
in
ARCHIVE_FORMATS
:
if
filename
[
-
len
(
ext
):]
==
ext
:
if
filename
[
-
len
(
ext
):]
==
ext
:
...
@@ -337,7 +427,12 @@ class PimpPackage:
...
@@ -337,7 +427,12 @@ class PimpPackage:
if
not
os
.
path
.
exists
(
setupname
)
and
not
NO_EXECUTE
:
if
not
os
.
path
.
exists
(
setupname
)
and
not
NO_EXECUTE
:
return
"no setup.py found after unpack of archive"
return
"no setup.py found after unpack of archive"
def
installSinglePackage
(
self
,
output
):
def
installSinglePackage
(
self
,
output
=
None
):
"""Download, unpack and install a single package.
If output is given it should be a file-like object and it
will receive a log of what happened."""
if
not
self
.
downloadURL
:
if
not
self
.
downloadURL
:
return
"%s: This package needs to be installed manually"
%
_fmtpackagename
(
self
)
return
"%s: This package needs to be installed manually"
%
_fmtpackagename
(
self
)
msg
=
self
.
downloadSinglePackage
(
output
)
msg
=
self
.
downloadSinglePackage
(
output
)
...
@@ -359,6 +454,9 @@ class PimpPackage:
...
@@ -359,6 +454,9 @@ class PimpPackage:
return
None
return
None
class
PimpInstaller
:
class
PimpInstaller
:
"""Installer engine: computes dependencies and installs
packages in the right order."""
def
__init__
(
self
,
db
):
def
__init__
(
self
,
db
):
self
.
_todo
=
[]
self
.
_todo
=
[]
self
.
_db
=
db
self
.
_db
=
db
...
@@ -374,6 +472,12 @@ class PimpInstaller:
...
@@ -374,6 +472,12 @@ class PimpInstaller:
self
.
_todo
.
insert
(
0
,
package
)
self
.
_todo
.
insert
(
0
,
package
)
def
_prepareInstall
(
self
,
package
,
force
=
0
,
recursive
=
1
):
def
_prepareInstall
(
self
,
package
,
force
=
0
,
recursive
=
1
):
"""Internal routine, recursive engine for prepareInstall.
Test whether the package is installed and (if not installed
or if force==1) prepend it to the temporary todo list and
call ourselves recursively on all prerequisites."""
if
not
force
:
if
not
force
:
status
,
message
=
package
.
installed
()
status
,
message
=
package
.
installed
()
if
status
==
"yes"
:
if
status
==
"yes"
:
...
@@ -391,6 +495,15 @@ class PimpInstaller:
...
@@ -391,6 +495,15 @@ class PimpInstaller:
self
.
_curmessages
.
append
(
"Requires: %s"
%
descr
)
self
.
_curmessages
.
append
(
"Requires: %s"
%
descr
)
def
prepareInstall
(
self
,
package
,
force
=
0
,
recursive
=
1
):
def
prepareInstall
(
self
,
package
,
force
=
0
,
recursive
=
1
):
"""Prepare installation of a package.
If the package is already installed and force is false nothing
is done. If recursive is true prerequisites are installed first.
Returns a list of packages (to be passed to install) and a list
of messages of any problems encountered.
"""
self
.
_curtodo
=
[]
self
.
_curtodo
=
[]
self
.
_curmessages
=
[]
self
.
_curmessages
=
[]
self
.
_prepareInstall
(
package
,
force
,
recursive
)
self
.
_prepareInstall
(
package
,
force
,
recursive
)
...
@@ -400,6 +513,8 @@ class PimpInstaller:
...
@@ -400,6 +513,8 @@ class PimpInstaller:
return
rv
return
rv
def
install
(
self
,
packages
,
output
):
def
install
(
self
,
packages
,
output
):
"""Install a list of packages."""
self
.
_addPackages
(
packages
)
self
.
_addPackages
(
packages
)
status
=
[]
status
=
[]
for
pkg
in
self
.
_todo
:
for
pkg
in
self
.
_todo
:
...
@@ -410,6 +525,11 @@ class PimpInstaller:
...
@@ -410,6 +525,11 @@ class PimpInstaller:
def
_fmtpackagename
(
dict
):
def
_fmtpackagename
(
dict
):
"""Return the full name "name-version-flavor" of a package.
If the package is a pseudo-package, something that cannot be
installed through pimp, return the name in (parentheses)."""
if
isinstance
(
dict
,
PimpPackage
):
if
isinstance
(
dict
,
PimpPackage
):
dict
=
dict
.
dump
()
dict
=
dict
.
dump
()
rv
=
dict
[
'name'
]
rv
=
dict
[
'name'
]
...
@@ -423,6 +543,8 @@ def _fmtpackagename(dict):
...
@@ -423,6 +543,8 @@ def _fmtpackagename(dict):
return
rv
return
rv
def
_run
(
mode
,
verbose
,
force
,
args
):
def
_run
(
mode
,
verbose
,
force
,
args
):
"""Engine for the main program"""
prefs
=
PimpPreferences
()
prefs
=
PimpPreferences
()
prefs
.
check
()
prefs
.
check
()
db
=
PimpDatabase
(
prefs
)
db
=
PimpDatabase
(
prefs
)
...
@@ -495,6 +617,8 @@ def _run(mode, verbose, force, args):
...
@@ -495,6 +617,8 @@ def _run(mode, verbose, force, args):
print
"
\
t
"
,
m
print
"
\
t
"
,
m
def
main
():
def
main
():
"""Minimal commandline tool to drive pimp."""
import
getopt
import
getopt
def
_help
():
def
_help
():
print
"Usage: pimp [-v] -s [package ...] List installed status"
print
"Usage: pimp [-v] -s [package ...] List installed status"
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment