Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
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
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
d8eaafb3
Commit
d8eaafb3
authored
May 17, 2001
by
Shane Hathaway
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
The Refresh product.
parent
1e9da134
Changes
7
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
888 additions
and
40 deletions
+888
-40
doc/CHANGES.txt
doc/CHANGES.txt
+4
-0
lib/python/App/Product.py
lib/python/App/Product.py
+135
-0
lib/python/App/RefreshFuncs.py
lib/python/App/RefreshFuncs.py
+456
-0
lib/python/App/dtml/refresh.dtml
lib/python/App/dtml/refresh.dtml
+118
-0
lib/python/OFS/Application.py
lib/python/OFS/Application.py
+107
-39
lib/python/Products/OFSP/help/Product_Refresh.stx
lib/python/Products/OFSP/help/Product_Refresh.stx
+62
-0
lib/python/Zope/__init__.py
lib/python/Zope/__init__.py
+6
-1
No files found.
doc/CHANGES.txt
View file @
d8eaafb3
...
...
@@ -57,6 +57,10 @@ Zope changes
- DTML-In and DTML-Tree now have optional "prefix" attributes
that can be used to make friendlier tag variable names.
- Added product reloading capability, formerly provided by
the "Refresh" product. This enables developers to see the
effect of changes to their products without restarting Zope.
Zope 2.3.0 beta 3
Features Added
...
...
lib/python/App/Product.py
View file @
d8eaafb3
...
...
@@ -116,6 +116,7 @@ from Factory import Factory
from
Permission
import
PermissionManager
import
ZClasses
,
ZClasses
.
ZClass
from
HelpSys.HelpSys
import
ProductHelp
import
RefreshFuncs
class
ProductFolder
(
Folder
):
...
...
@@ -341,6 +342,129 @@ class Product(Folder, PermissionManager):
self
.
_setObject
(
'Help'
,
ProductHelp
(
'Help'
,
self
.
id
))
return
self
.
Help
#
# Product refresh
#
_refresh_dtml
=
Globals
.
DTMLFile
(
'dtml/refresh'
,
globals
())
def
_readRefreshTxt
(
self
,
pid
=
None
):
refresh_txt
=
None
if
pid
is
None
:
pid
=
self
.
id
for
productDir
in
Products
.
__path__
:
found
=
0
for
name
in
(
'refresh.txt'
,
'REFRESH.txt'
,
'REFRESH.TXT'
):
p
=
os
.
path
.
join
(
productDir
,
pid
,
name
)
if
os
.
path
.
exists
(
p
):
found
=
1
break
if
found
:
try
:
file
=
open
(
p
)
text
=
file
.
read
()
file
.
close
()
refresh_txt
=
text
break
except
:
# Not found here.
pass
return
refresh_txt
def
manage_refresh
(
self
,
REQUEST
,
manage_tabs_message
=
None
):
'''
Displays the refresh management screen.
'''
error_type
=
error_value
=
error_tb
=
None
exc
=
RefreshFuncs
.
getLastRefreshException
(
self
.
id
)
if
exc
is
not
None
:
error_type
,
error_value
,
error_tb
=
exc
exc
=
None
refresh_txt
=
self
.
_readRefreshTxt
()
# Read the persistent refresh information.
auto
=
RefreshFuncs
.
isAutoRefreshEnabled
(
self
.
_p_jar
,
self
.
id
)
deps
=
RefreshFuncs
.
getDependentProducts
(
self
.
_p_jar
,
self
.
id
)
# List all product modules.
mods
=
RefreshFuncs
.
listRefreshableModules
(
self
.
id
)
loaded_modules
=
[]
prefix
=
'Products.%s'
%
self
.
id
prefixdot
=
prefix
+
'.'
lpdot
=
len
(
prefixdot
)
for
name
,
module
in
mods
:
if
name
==
prefix
or
name
[:
lpdot
]
==
prefixdot
:
name
=
name
[
lpdot
:]
if
not
name
:
name
=
'__init__'
loaded_modules
.
append
(
name
)
all_auto
=
RefreshFuncs
.
listAutoRefreshableProducts
(
self
.
_p_jar
)
for
pid
in
all_auto
:
# Ignore products that don't have a refresh.txt.
if
self
.
_readRefreshTxt
(
pid
)
is
None
:
all_auto
.
remove
(
pid
)
auto_other
=
filter
(
lambda
productId
,
myId
=
self
.
id
:
productId
!=
myId
,
all_auto
)
# Return rendered DTML.
return
self
.
_refresh_dtml
(
REQUEST
,
id
=
self
.
id
,
refresh_txt
=
refresh_txt
,
error_type
=
error_type
,
error_value
=
error_value
,
error_tb
=
error_tb
,
devel_mode
=
Globals
.
DevelopmentMode
,
auto_refresh_enabled
=
auto
,
auto_refresh_other
=
auto_other
,
dependent_products
=
deps
,
loaded_modules
=
loaded_modules
,
manage_tabs_message
=
manage_tabs_message
,
management_view
=
'Refresh'
)
def
manage_performRefresh
(
self
,
REQUEST
=
None
):
'''
Attempts to perform a refresh operation.
'''
if
self
.
_readRefreshTxt
()
is
None
:
raise
'Unauthorized'
,
'refresh.txt not found'
message
=
None
if
RefreshFuncs
.
performFullRefresh
(
self
.
_p_jar
,
self
.
id
):
from
ZODB
import
Connection
Connection
.
updateCodeTimestamp
()
# Clears cache in next connection.
message
=
'Product refreshed.'
else
:
message
=
'An exception occurred.'
if
REQUEST
is
not
None
:
return
self
.
manage_refresh
(
REQUEST
,
manage_tabs_message
=
message
)
def
manage_enableAutoRefresh
(
self
,
enable
=
0
,
REQUEST
=
None
):
'''
Changes the auto refresh flag for this product.
'''
if
self
.
_readRefreshTxt
()
is
None
:
raise
'Unauthorized'
,
'refresh.txt not created'
RefreshFuncs
.
enableAutoRefresh
(
self
.
_p_jar
,
self
.
id
,
enable
)
if
enable
:
message
=
'Enabled auto refresh.'
else
:
message
=
'Disabled auto refresh.'
if
REQUEST
is
not
None
:
return
self
.
manage_refresh
(
REQUEST
,
manage_tabs_message
=
message
)
def
manage_selectDependentProducts
(
self
,
selections
=
(),
REQUEST
=
None
):
'''
Selects which products to refresh simultaneously.
'''
if
self
.
_readRefreshTxt
()
is
None
:
raise
'Unauthorized'
,
'refresh.txt not created'
RefreshFuncs
.
setDependentProducts
(
self
.
_p_jar
,
self
.
id
,
selections
)
if
REQUEST
is
not
None
:
return
self
.
manage_refresh
(
REQUEST
)
class
CompressedOutputFile
:
def
__init__
(
self
,
rot
):
self
.
_c
=
zlib
.
compressobj
()
...
...
@@ -477,6 +601,17 @@ def initializeProduct(productp, name, home, app):
)
break
# Ensure this product has a refresh tab.
found
=
0
for
option
in
product
.
manage_options
:
if
option
.
get
(
'label'
)
==
'Refresh'
:
found
=
1
break
if
not
found
:
product
.
manage_options
=
product
.
manage_options
+
(
{
'label'
:
'Refresh'
,
'action'
:
'manage_refresh'
,
'help'
:
(
'OFSP'
,
'Product_Refresh.stx'
)},)
if
(
os
.
environ
.
get
(
'ZEO_CLIENT'
)
and
not
os
.
environ
.
get
(
'FORCE_PRODUCT_LOAD'
)):
get_transaction
().
abort
()
...
...
lib/python/App/RefreshFuncs.py
0 → 100644
View file @
d8eaafb3
##############################################################################
#
# Zope Public License (ZPL) Version 1.0
# -------------------------------------
#
# Copyright (c) Digital Creations. All rights reserved.
#
# This license has been certified as Open Source(tm).
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions in source code must retain the above copyright
# notice, this list of conditions, and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions, and the following disclaimer in
# the documentation and/or other materials provided with the
# distribution.
#
# 3. Digital Creations requests that attribution be given to Zope
# in any manner possible. Zope includes a "Powered by Zope"
# button that is installed by default. While it is not a license
# violation to remove this button, it is requested that the
# attribution remain. A significant investment has been put
# into Zope, and this effort will continue if the Zope community
# continues to grow. This is one way to assure that growth.
#
# 4. All advertising materials and documentation mentioning
# features derived from or use of this software must display
# the following acknowledgement:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# In the event that the product being advertised includes an
# intact Zope distribution (with copyright and license included)
# then this clause is waived.
#
# 5. Names associated with Zope or Digital Creations must not be used to
# endorse or promote products derived from this software without
# prior written permission from Digital Creations.
#
# 6. Modified redistributions of any form whatsoever must retain
# the following acknowledgment:
#
# "This product includes software developed by Digital Creations
# for use in the Z Object Publishing Environment
# (http://www.zope.org/)."
#
# Intact (re-)distributions of any official Zope release do not
# require an external acknowledgement.
#
# 7. Modifications are encouraged but must be packaged separately as
# patches to official Zope releases. Distributions that do not
# clearly separate the patches from the original work must be clearly
# labeled as unofficial distributions. Modifications which do not
# carry the name Zope may be packaged in any form, as long as they
# conform to all of the clauses above.
#
#
# Disclaimer
#
# THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
#
# This software consists of contributions made by Digital Creations and
# many individuals on behalf of Digital Creations. Specific
# attributions are listed in the accompanying credits file.
#
##############################################################################
'''
Functions for refreshing products.
$Id: RefreshFuncs.py,v 1.1 2001/05/17 18:35:08 shane Exp $
'''
import
os
,
sys
from
time
import
time
from
string
import
split
,
join
import
Products
from
ExtensionClass
import
Base
from
Globals
import
PersistentMapping
from
zLOG
import
format_exception
,
LOG
,
ERROR
,
INFO
global_classes_timestamp
=
0
products_mod_times
=
{}
_marker
=
[]
# create a new marker object.
refresh_exc_info
=
{}
class
dummyClass
:
pass
class
dummyClass2
(
Base
):
pass
def
dummyFunc
():
pass
ClassTypes
=
(
type
(
dummyClass
),
type
(
dummyClass2
))
ModuleType
=
type
(
sys
)
FuncType
=
type
(
dummyFunc
)
next_auto_refresh_check
=
0
AUTO_REFRESH_INTERVAL
=
2
# 2 seconds.
# Functions for storing and retrieving the auto-refresh state for
# each product.
def
_getCentralRefreshData
(
jar
,
create
=
0
):
root
=
jar
.
root
()
if
root
.
has_key
(
'RefreshData'
):
rd
=
root
[
'RefreshData'
]
else
:
rd
=
PersistentMapping
()
if
create
:
root
[
'RefreshData'
]
=
rd
return
rd
def
isAutoRefreshEnabled
(
jar
,
productid
):
rd
=
_getCentralRefreshData
(
jar
)
ids
=
rd
.
get
(
'auto'
,
None
)
if
ids
:
return
ids
.
get
(
productid
,
0
)
else
:
return
0
def
enableAutoRefresh
(
jar
,
productid
,
enable
):
productid
=
str
(
productid
)
rd
=
_getCentralRefreshData
(
jar
,
1
)
ids
=
rd
.
get
(
'auto'
,
None
)
if
ids
is
None
:
if
enable
:
rd
[
'auto'
]
=
ids
=
PersistentMapping
()
else
:
return
if
enable
:
ids
[
productid
]
=
1
else
:
if
ids
.
has_key
(
productid
):
del
ids
[
productid
]
def
listAutoRefreshableProducts
(
jar
):
rd
=
_getCentralRefreshData
(
jar
)
auto
=
rd
.
get
(
'auto'
,
None
)
if
auto
:
ids
=
[]
for
k
,
v
in
auto
.
items
():
if
v
:
ids
.
append
(
k
)
return
ids
else
:
return
()
def
getDependentProducts
(
jar
,
productid
):
rd
=
_getCentralRefreshData
(
jar
)
products
=
rd
.
get
(
'products'
,
None
)
if
products
is
None
:
return
()
product
=
products
.
get
(
productid
,
None
)
if
product
is
None
:
return
()
return
product
.
get
(
'dependent_products'
,
())
def
setDependentProducts
(
jar
,
productid
,
dep_ids
):
productid
=
str
(
productid
)
rd
=
_getCentralRefreshData
(
jar
,
1
)
products
=
rd
.
get
(
'products'
,
None
)
if
products
is
None
:
rd
[
'products'
]
=
products
=
PersistentMapping
()
product
=
products
.
get
(
productid
,
None
)
if
product
is
None
:
products
[
productid
]
=
product
=
PersistentMapping
()
product
[
'dependent_products'
]
=
tuple
(
map
(
str
,
dep_ids
))
# Functions for sorting modules by dependency.
def
listRequiredModulesByClass
(
klass
,
rval
):
if
hasattr
(
klass
,
'__module__'
):
rval
[
klass
.
__module__
]
=
1
# klass.__module__ is a string.
if
hasattr
(
klass
,
'__bases__'
):
for
b
in
klass
.
__bases__
:
listRequiredModulesByClass
(
b
,
rval
)
def
listRequiredModules
(
module
):
rval
=
{}
if
hasattr
(
module
,
'__dict__'
):
for
key
,
value
in
module
.
__dict__
.
items
():
t
=
type
(
value
)
if
t
in
ClassTypes
:
listRequiredModulesByClass
(
value
,
rval
)
elif
t
is
ModuleType
and
hasattr
(
value
,
'__name__'
):
rval
[
value
.
__name__
]
=
1
elif
t
is
FuncType
and
value
.
func_globals
.
has_key
(
'__name__'
):
rval
[
value
.
func_globals
[
'__name__'
]]
=
1
return
rval
def
sortModulesByDependency
(
modlist
):
unchosen
=
{}
for
name
,
module
in
modlist
:
unchosen
[
name
]
=
(
module
,
listRequiredModules
(
module
))
chose
=
1
rval
=
[]
while
chose
:
chose
=
0
for
name
,
(
module
,
req
)
in
unchosen
.
items
():
all_satisfied
=
1
for
n
in
unchosen
.
keys
():
if
name
==
n
:
continue
# Skip self.
if
req
.
has_key
(
n
):
# There is still a dependency. Can't
# include this module in the list yet.
all_satisfied
=
0
break
if
all_satisfied
:
chose
=
1
rval
.
append
((
name
,
module
))
del
unchosen
[
name
]
# There might be some modules left over that are interdependent.
for
name
,
(
module
,
req
)
in
unchosen
.
items
():
rval
.
append
((
name
,
module
))
return
rval
# Functions for performing refresh.
def
getReloadVar
(
module
):
reload_var
=
getattr
(
module
,
'__refresh_module__'
,
_marker
)
if
reload_var
is
_marker
:
reload_var
=
getattr
(
module
,
'__reload_module__'
,
_marker
)
if
reload_var
is
_marker
:
reload_var
=
1
return
reload_var
def
listRefreshableModules
(
productid
):
prefix
=
"Products.%s"
%
productid
prefixdot
=
prefix
+
'.'
lpdot
=
len
(
prefixdot
)
rval
=
[]
for
name
,
module
in
sys
.
modules
.
items
():
if
module
and
(
name
==
prefix
or
name
[:
lpdot
]
==
prefixdot
):
reload_var
=
getReloadVar
(
module
)
if
callable
(
reload_var
)
or
reload_var
:
rval
.
append
((
name
,
module
))
return
rval
def
logBadRefresh
(
productid
):
exc
=
sys
.
exc_info
()
try
:
LOG
(
'Refresh'
,
ERROR
,
'Exception while refreshing %s'
%
productid
,
error
=
exc
)
if
hasattr
(
exc
[
0
],
'__name__'
):
error_type
=
exc
[
0
].
__name__
else
:
error_type
=
str
(
exc
[
0
])
error_value
=
str
(
exc
[
1
])
info
=
format_exception
(
exc
[
0
],
exc
[
1
],
exc
[
2
],
limit
=
200
)
refresh_exc_info
[
productid
]
=
(
error_type
,
error_value
,
info
)
finally
:
exc
=
None
def
performRefresh
(
jar
,
productid
):
'''Attempts to perform a refresh operation.
'''
refresh_exc_info
[
productid
]
=
None
setupModTimes
(
productid
)
# Refresh again only if changed again.
modlist
=
listRefreshableModules
(
productid
)
modlist
=
sortModulesByDependency
(
modlist
)
for
name
,
module
in
modlist
:
# Remove the __import_error__ attribute.
try
:
del
module
.
__import_error__
except
:
pass
# Ask the module how it should be reloaded.
reload_var
=
getReloadVar
(
module
)
if
callable
(
reload_var
):
try
:
reload_var
()
except
:
logBadRefresh
(
productid
)
return
0
else
:
try
:
reload
(
module
)
except
:
logBadRefresh
(
productid
)
return
0
# Reinitialize and reinstall the product.
from
OFS
import
Application
Application
.
reimport_product
(
productid
)
app
=
jar
.
root
()[
'Application'
]
Application
.
reinstall_product
(
app
,
productid
)
return
1
def
performSafeRefresh
(
jar
,
productid
):
try
:
LOG
(
'Refresh'
,
INFO
,
'Refreshing product %s'
%
productid
)
if
not
performRefresh
(
jar
,
productid
):
return
0
except
:
logBadRefresh
(
productid
)
return
0
else
:
return
1
def
performFullRefresh
(
jar
,
productid
):
if
performSafeRefresh
(
jar
,
productid
):
dep_ids
=
getDependentProducts
(
jar
,
productid
)
for
dep_id
in
dep_ids
:
if
isAutoRefreshEnabled
(
jar
,
dep_id
):
if
not
performSafeRefresh
(
jar
,
dep_id
):
return
0
else
:
return
0
return
1
def
getLastRefreshException
(
productid
):
return
refresh_exc_info
.
get
(
productid
,
None
)
# Functions for quickly scanning the dates of product modules.
def
tryFindProductDirectory
(
productid
):
path_join
=
os
.
path
.
join
isdir
=
os
.
path
.
isdir
exists
=
os
.
path
.
exists
for
products_dir
in
Products
.
__path__
:
product_dir
=
path_join
(
products_dir
,
productid
)
if
not
isdir
(
product_dir
):
continue
if
not
exists
(
path_join
(
product_dir
,
'__init__.py'
)):
if
not
exists
(
path_join
(
product_dir
,
'__init__.pyc'
)):
continue
return
product_dir
return
None
def
tryFindModuleFilename
(
product_dir
,
filename
):
# Try different variations of the filename of a module.
path_join
=
os
.
path
.
join
isdir
=
os
.
path
.
isdir
exists
=
os
.
path
.
exists
found
=
None
fn
=
path_join
(
product_dir
,
filename
+
'.py'
)
if
exists
(
fn
):
found
=
fn
if
not
found
:
fn
=
fn
+
'c'
if
exists
(
fn
):
found
=
fn
if
not
found
:
fn
=
path_join
(
product_dir
,
filename
)
if
isdir
(
fn
):
fn
=
path_join
(
fn
,
'__init__.py'
)
if
exists
(
fn
):
found
=
fn
else
:
fn
=
fn
+
'c'
if
exists
(
fn
):
found
=
fn
return
found
def
setupModTimes
(
productid
):
mod_times
=
[]
product_dir
=
tryFindProductDirectory
(
productid
)
if
product_dir
is
not
None
:
modlist
=
listRefreshableModules
(
productid
)
path_join
=
os
.
path
.
join
exists
=
os
.
path
.
exists
for
name
,
module
in
modlist
:
splitname
=
split
(
name
,
'.'
)[
2
:]
if
not
splitname
:
filename
=
'__init__'
else
:
filename
=
apply
(
path_join
,
splitname
)
found
=
tryFindModuleFilename
(
product_dir
,
filename
)
if
found
:
try
:
mtime
=
os
.
stat
(
found
)[
8
]
except
:
mtime
=
0
mod_times
.
append
((
found
,
mtime
))
products_mod_times
[
productid
]
=
mod_times
def
checkModTimes
(
productid
):
# Returns 1 if there were changes.
mod_times
=
products_mod_times
.
get
(
productid
,
None
)
if
mod_times
is
None
:
# Initialize the mod times.
setupModTimes
(
productid
)
return
0
for
filename
,
mod_time
in
mod_times
:
try
:
mtime
=
os
.
stat
(
filename
)[
8
]
except
:
mtime
=
0
if
mtime
!=
mod_time
:
# Something changed!
return
1
return
0
# Functions for performing auto-refresh.
def
checkAutoRefresh
(
jar
):
# Note: this function is NOT allowed to change the database!
global
next_auto_refresh_check
now
=
time
()
if
next_auto_refresh_check
and
next_auto_refresh_check
>
now
:
# Not enough time has passed.
return
()
next_auto_refresh_check
=
now
+
AUTO_REFRESH_INTERVAL
rd
=
_getCentralRefreshData
(
jar
)
ids
=
rd
.
get
(
'auto'
,
None
)
if
not
ids
:
return
()
auto_refresh_ids
=
[]
for
productid
in
ids
.
keys
():
if
checkModTimes
(
productid
):
auto_refresh_ids
.
append
(
productid
)
return
auto_refresh_ids
def
finishAutoRefresh
(
jar
,
productids
):
# This function is allowed to change the database.
for
productid
in
productids
:
performFullRefresh
(
jar
,
productid
)
def
autoRefresh
(
jar
):
# Must be called before there are any changes made
# by the connection to the database!
auto_refresh_ids
=
checkAutoRefresh
(
jar
)
if
auto_refresh_ids
:
finishAutoRefresh
(
jar
,
auto_refresh_ids
)
from
ZODB
import
Connection
Connection
.
updateCodeTimestamp
()
get_transaction
().
commit
()
jar
.
_resetCache
()
get_transaction
().
begin
()
def
setupAutoRefresh
(
jar
):
# Install hook.
from
ZODB.ZApplication
import
connection_open_hooks
connection_open_hooks
.
append
(
autoRefresh
)
# Init mod times.
checkAutoRefresh
(
jar
)
lib/python/App/dtml/refresh.dtml
0 → 100644
View file @
d8eaafb3
<dtml-comment>
Arguments for this method:
id, refresh_txt, error_type, error_value, error_tb, devel_mode,
auto_refresh_enabled, auto_refresh_other, dependent_products,
loaded_modules
</dtml-comment>
<dtml-let
form_title=
"'Refresh product: ' + id"
>
<dtml-if
manage_page_header
>
<dtml-var
manage_page_header
>
<dtml-else>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html
lang=
"en"
>
<head>
<title>
&dtml-form_title;
</title>
</head>
<body
bgcolor=
"#FFFFFF"
link=
"#000099"
vlink=
"#555555"
>
<h3>
&dtml-form_title;
</h3>
</dtml-if>
</dtml-let>
<dtml-var
manage_tabs
>
<dtml-if
expr=
"refresh_txt == _.None"
>
<p>
The refresh function, designed to ease the development of Zope
products, is not currently enabled for this product.
To make it available, put a file named "refresh.txt" in the
&dtml-id;
product directory. Please note that not all products are
compatible with the refresh function.
</p>
<dtml-else>
<dtml-if
error_type
>
<p><b>
An exception occurred during the last refresh.
</b><br
/>
Exception type:
<b>
&dtml-error_type;
</b>
<br
/>
Exception value:
<b>
&dtml-error_value;
</b>
</p>
<pre>
&dtml-error_tb;
</pre>
<hr
/>
</dtml-if>
<form
action=
"&dtml-absolute_url;"
method=
"POST"
>
<table
border=
"0"
>
<tr>
<td
valign=
"top"
>
<dtml-if
expr=
"_.string.strip(refresh_txt)"
>
<p>
<b>
Important information about refreshing this product:
</b><br
/>
<dtml-var
refresh_txt
fmt=
"structured-text"
>
</p>
</dtml-if>
<div
align=
"center"
><input
type=
"submit"
name=
"manage_performRefresh:method"
value=
"Refresh this product"
/>
</div>
<p>
<dtml-if
auto_refresh_enabled
>
<dtml-if
devel_mode
>
Auto refresh is enabled. Zope will repeatedly scan for
changes to the Python modules that make up this product and
execute a refresh when needed.
<dtml-else>
Although auto refresh is enabled, Zope is not in development
mode so auto refresh is not available. Use the "-D" argument
when starting Zope to enable development mode.
</dtml-if>
<dtml-else>
Auto refresh is disabled. Enable auto refresh
to cause Zope to frequently scan this product for changes.
Note that auto refresh can slow down Zope considerably
if enabled for more than a few products.
</dtml-if>
<br
/>
<dtml-let
checked=
"auto_refresh_enabled and 'checked' or ' '"
>
<input
type=
"checkbox"
name=
"enable"
value=
"1"
&
dtml-checked
;
/>
Auto refresh mode
<input
type=
"submit"
name=
"manage_enableAutoRefresh:method"
value=
"Change"
/>
</dtml-let>
</p>
<dtml-if
auto_refresh_other
>
<p>
Select dependent auto-refreshable products to be refreshed
simultaneously.
<br
/>
<dtml-in
auto_refresh_other
sort
>
<dtml-let
checked=
"(_['sequence-item'] in dependent_products) and
'checked' or ' '"
>
<input
type=
"checkbox"
name=
"selections:list"
value=
"&dtml-sequence-item;"
&
dtml-checked
;
/>
<a
href=
"../&dtml-sequence-item;/manage_refresh"
>
&dtml-sequence-item;
</a><br
/>
</dtml-let>
</dtml-in>
<input
type=
"submit"
name=
"manage_selectDependentProducts:method"
value=
"Change"
/>
</p>
</dtml-if>
</td>
<td
valign=
"top"
class=
"row-hilite"
>
<p><b>
Refreshable product modules:
</b></p>
<ul>
<dtml-in
loaded_modules
sort
>
<li>
&dtml-sequence-item;
</li>
</dtml-in>
</ul>
</td>
</tr>
</table>
</form>
</dtml-if>
<dtml-if
manage_page_footer
>
<dtml-var
manage_page_footer
>
<dtml-else>
</body></html>
</dtml-if>
lib/python/OFS/Application.py
View file @
d8eaafb3
...
...
@@ -85,8 +85,8 @@
__doc__
=
'''Application support
$Id: Application.py,v 1.14
3 2001/04/07 16:37:07 jim
Exp $'''
__version__
=
'$Revision: 1.14
3
$'
[
11
:
-
2
]
$Id: Application.py,v 1.14
4 2001/05/17 18:35:09 shane
Exp $'''
__version__
=
'$Revision: 1.14
4
$'
[
11
:
-
2
]
import
Globals
,
Folder
,
os
,
sys
,
App
.
Product
,
App
.
ProductRegistry
,
misc_
import
time
,
traceback
,
os
,
string
,
Products
...
...
@@ -495,16 +495,8 @@ def initialize(app):
get_transaction
().
abort
()
def
import_products
(
_st
=
type
(
''
)
):
def
import_products
():
# Try to import each product, checking for and catching errors.
path_join
=
os
.
path
.
join
isdir
=
os
.
path
.
isdir
exists
=
os
.
path
.
exists
DictType
=
type
({})
global_dict
=
globals
()
silly
=
(
'__doc__'
,)
modules
=
sys
.
modules
have_module
=
modules
.
has_key
done
=
{}
for
product_dir
in
Products
.
__path__
:
...
...
@@ -516,12 +508,26 @@ def import_products(_st=type('')):
if
done
.
has_key
(
product_name
):
continue
done
[
product_name
]
=
1
import_product
(
product_dir
,
product_name
)
def
import_product
(
product_dir
,
product_name
,
raise_exc
=
0
,
log_exc
=
1
):
path_join
=
os
.
path
.
join
isdir
=
os
.
path
.
isdir
exists
=
os
.
path
.
exists
_st
=
type
(
''
)
global_dict
=
globals
()
silly
=
(
'__doc__'
,)
modules
=
sys
.
modules
have_module
=
modules
.
has_key
if
1
:
# Preserve indentation for diff :-)
try
:
package_dir
=
path_join
(
product_dir
,
product_name
)
if
not
isdir
(
package_dir
):
continue
if
not
isdir
(
package_dir
):
return
if
not
exists
(
path_join
(
package_dir
,
'__init__.py'
)):
if
not
exists
(
path_join
(
package_dir
,
'__init__.pyc'
)):
continue
return
pname
=
"Products.%s"
%
product_name
try
:
...
...
@@ -532,35 +538,27 @@ def import_products(_st=type('')):
if
type
(
v
)
is
_st
and
have_module
(
v
):
v
=
modules
[
v
]
modules
[
k
]
=
v
except
:
LOG
(
'Zope'
,
ERROR
,
'Couldn
\
'
t import %s'
%
pname
,
error
=
sys
.
exc_info
())
exc
=
sys
.
exc_info
()
if
log_exc
:
LOG
(
'Zope'
,
ERROR
,
'Could not import %s'
%
pname
,
error
=
exc
)
f
=
StringIO
()
traceback
.
print_exc
(
100
,
f
)
f
=
f
.
getvalue
()
try
:
modules
[
pname
].
__import_error__
=
f
except
:
pass
if
raise_exc
:
raise
exc
[
0
],
exc
[
1
],
exc
[
2
]
finally
:
exc
=
None
def
install_products
(
app
):
# Install a list of products into the basic folder class, so
# that all folders know about top-level objects, aka products
path_join
=
os
.
path
.
join
isdir
=
os
.
path
.
isdir
exists
=
os
.
path
.
exists
DictType
=
type
({})
from
Folder
import
Folder
folder_permissions
=
{}
for
p
in
Folder
.
__ac_permissions__
:
permission
,
names
=
p
[:
2
]
folder_permissions
[
permission
]
=
names
folder_permissions
=
get_folder_permissions
()
meta_types
=
[]
global_dict
=
globals
()
silly
=
(
'__doc__'
,)
done
=
{}
get_transaction
().
note
(
'Prior to product installs'
)
...
...
@@ -579,13 +577,38 @@ def install_products(app):
if
done
.
has_key
(
product_name
):
continue
done
[
product_name
]
=
1
install_product
(
app
,
product_dir
,
product_name
,
meta_types
,
folder_permissions
)
Products
.
meta_types
=
Products
.
meta_types
+
tuple
(
meta_types
)
Globals
.
default__class_init__
(
Folder
.
Folder
)
def
get_folder_permissions
():
folder_permissions
=
{}
for
p
in
Folder
.
Folder
.
__ac_permissions__
:
permission
,
names
=
p
[:
2
]
folder_permissions
[
permission
]
=
names
return
folder_permissions
def
install_product
(
app
,
product_dir
,
product_name
,
meta_types
,
folder_permissions
,
raise_exc
=
0
,
log_exc
=
1
):
path_join
=
os
.
path
.
join
isdir
=
os
.
path
.
isdir
exists
=
os
.
path
.
exists
DictType
=
type
({})
global_dict
=
globals
()
silly
=
(
'__doc__'
,)
if
1
:
# Preserve indentation for diff :-)
package_dir
=
path_join
(
product_dir
,
product_name
)
__traceback_info__
=
product_name
if
not
isdir
(
package_dir
):
continue
if
not
isdir
(
package_dir
):
return
if
not
exists
(
path_join
(
package_dir
,
'__init__.py'
)):
if
not
exists
(
path_join
(
package_dir
,
'__init__.pyc'
)):
continue
return
try
:
product
=
__import__
(
"Products.%s"
%
product_name
,
global_dict
,
global_dict
,
silly
)
...
...
@@ -661,8 +684,8 @@ def install_products(app):
for
permission
,
names
in
new_permissions
:
folder_permissions
[
permission
]
=
names
new_permissions
.
sort
()
Folder
.
__dict__
[
'__ac_permissions__'
]
=
tuple
(
list
(
Folder
.
__ac_permissions__
)
+
new_permissions
)
Folder
.
Folder
.
__dict__
[
'__ac_permissions__'
]
=
tuple
(
list
(
Folder
.
Folder
.
__ac_permissions__
)
+
new_permissions
)
if
(
os
.
environ
.
get
(
'ZEO_CLIENT'
)
and
not
os
.
environ
.
get
(
'FORCE_PRODUCT_LOAD'
)):
...
...
@@ -674,13 +697,58 @@ def install_products(app):
get_transaction
().
commit
()
except
:
if
log_exc
:
LOG
(
'Zope'
,
ERROR
,
'Couldn
\
'
t install %s'
%
product_name
,
error
=
sys
.
exc_info
())
get_transaction
().
abort
()
if
raise_exc
:
raise
def
reinstall_product
(
app
,
product_name
):
folder_permissions
=
get_folder_permissions
()
meta_types
=
[]
get_transaction
().
note
(
'Prior to product reinstall'
)
get_transaction
().
commit
()
for
product_dir
in
Products
.
__path__
:
product_names
=
os
.
listdir
(
product_dir
)
product_names
.
sort
()
if
product_name
in
product_names
:
removeProductMetaTypes
(
product_name
)
install_product
(
app
,
product_dir
,
product_name
,
meta_types
,
folder_permissions
,
raise_exc
=
1
,
log_exc
=
0
)
Products
.
meta_types
=
Products
.
meta_types
+
tuple
(
meta_types
)
Globals
.
default__class_init__
(
Folder
.
Folder
)
Globals
.
default__class_init__
(
Folder
)
def
reimport_product
(
product_name
):
for
product_dir
in
Products
.
__path__
:
product_names
=
os
.
listdir
(
product_dir
)
product_names
.
sort
()
if
product_name
in
product_names
:
import_product
(
product_dir
,
product_name
,
raise_exc
=
1
,
log_exc
=
0
)
break
def
removeProductMetaTypes
(
pid
):
'''
Unregisters the meta types registered by a product.
'''
meta_types
=
Products
.
meta_types
new_mts
=
[]
changed
=
0
for
meta_type
in
meta_types
:
if
meta_type
.
get
(
'product'
,
None
)
==
pid
:
# Remove this meta type.
changed
=
1
else
:
new_mts
.
append
(
meta_type
)
if
changed
:
Products
.
meta_types
=
tuple
(
new_mts
)
def
pgetattr
(
product
,
name
,
default
=
install_products
,
__init__
=
0
):
...
...
lib/python/Products/OFSP/help/Product_Refresh.stx
0 → 100644
View file @
d8eaafb3
Product - Refresh: Reload a filesystem-based Python product.
Description
This view allows you to reload filesystem-based product code
without restarting Zope. This function is useful during
development of products.
To enable your product to be refreshed, it is required that you
put a file called 'refresh.txt' in your product directory.
It can optionally contain a warning for others who might use
the refresh function.
(Producting refreshing is not perfect. Some products, especially
"hotfix" style products which patch Zope, should not be refreshed.
That's why 'refresh.txt' is required. Most products are safe to
refresh, however.)
There are two modes of operation. You can visit your product's
'Refresh' tab and manually push the refresh button. Or you can
turn on "auto-refresh" mode, which causes Zope to periodically
scan the modification time of the Python files that make up your
product and execute a refresh operation in the background.
**NOTE**: Don't enable auto-refresh for too many products at once.
Scanning file modification times can take a lot of time per
request.
You can also select dependent refreshable products. If you have
a product that subclasses from a product you're working on,
you'll want to enable refresh for both products and add the
product that subclasses as a dependent of the product you're
modifying. This enables subclasses to be updated.
Controls
'Refresh this product' -- The manual refresh button.
'Auto refresh mode' -- Check the checkbox to enable auto-refresh.
'Dependent auto-refreshable products' -- A list of other products
which are auto-refreshable.
How it works
To execute a refresh, Zope looks in the sys.modules dictionary
for modules with names that start with the prefix for your product.
It tries to scan for dependencies between the modules that make
up your product then uses Python's reload() function for each
module in order. Then it sets a flag that will cause ZODB to dump
its cache on the next connection so that changes to persistent
classes will take effect.
To implement auto-refresh, Zope stores a PersistentMapping called
RefreshData on the database root object (below the Application
object). The contents of the PersistentMapping are examined at the
moment a database connection is opened by ZApplication. The
PersistentMapping contains a list of which products have auto-refresh
enabled. For each product with auto-refresh enabled, Zope compares
the file mod times with the last recorded times and executes a
refresh if there are any changes.
lib/python/Zope/__init__.py
View file @
d8eaafb3
...
...
@@ -142,13 +142,18 @@ app=bobo_application=ZODB.ZApplication.ZApplicationWrapper(
# Initialize products:
c
=
app
()
OFS
.
Application
.
initialize
(
c
)
if
Globals
.
DevelopmentMode
:
# Set up auto-refresh.
from
App.RefreshFuncs
import
setupAutoRefresh
setupAutoRefresh
(
c
.
_p_jar
)
c
.
_p_jar
.
close
()
del
c
# "Log off" as system user
AccessControl
.
SecurityManagement
.
noSecurityManager
()
# This is sneaky, but we don't want to play with Main:
sys
.
modules
[
'Main'
]
=
sys
.
modules
[
'Zope'
]
...
...
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