Как мне вызвать функцию или как ее обернуть, чтобы, если она занимает больше 5 секунд, скрипт отменяет ее?
Я отправил gist , который решает этот вопрос / проблему с декоратором и
threading.Timer
. Здесь он с разбивкой.Импорт и настройки для совместимости
Он был протестирован с Python 2 и 3. Он также должен работать под Unix / Linux и Windows.
Сначала импорт. Эти попытки сохранить код совместимым независимо от версии Python:
from __future__ import print_function import sys import threading from time import sleep try: import thread except ImportError: import _thread as thread
Использовать независимый от версии код:
try: range, _print = xrange, print def print(*args, **kwargs): flush = kwargs.pop('flush', False) _print(*args, **kwargs) if flush: kwargs.get('file', sys.stdout).flush() except NameError: pass
Теперь мы импортировали наши функции из стандартной библиотеки.
exit_after
decoratorДалее нам нужна функция для завершения
main()
из дочернего потока:def quit_function(fn_name): # print to stderr, unbuffered in Python 2. print('{0} took too long'.format(fn_name), file=sys.stderr) sys.stderr.flush() # Python 3 stderr is likely buffered. thread.interrupt_main() # raises KeyboardInterrupt
И вот сам декоратор :
def exit_after(s): ''' use as decorator to exit process if function takes longer than s seconds ''' def outer(fn): def inner(*args, **kwargs): timer = threading.Timer(s, quit_function, args=[fn.__name__]) timer.start() try: result = fn(*args, **kwargs) finally: timer.cancel() return result return inner return outer
Использование
И вот использование, которое напрямую отвечает на ваш вопрос об окончании через 5 секунд!:
@exit_after(5) def countdown(n): print('countdown started', flush=True) for i in range(n, -1, -1): print(i, end=', ', flush=True) sleep(1) print('countdown finished')
Демонстрация:
>>> countdown(3) countdown started 3, 2, 1, 0, countdown finished >>> countdown(10) countdown started 10, 9, 8, 7, 6, countdown took too long Traceback (most recent call last): File "
", line 1, in File " ", line 11, in inner File " ", line 6, in countdown KeyboardInterrupt Второй вызов функции не будет завершен, вместо этого процесс должен выйти с трассировкой!
KeyboardInterrupt
не всегда останавливает спальный потокОбратите внимание, что спящий режим не всегда прерывается прерыванием клавиатуры на Python 2 в Windows, например:
@exit_after(1) def sleep10(): sleep(10) print('slept 10 seconds') >>> sleep10() sleep10 took too long # Note that it hangs here about 9 more seconds Traceback (most recent call last): File "
", line 1, in File " ", line 11, in inner File " ", line 3, in sleep10 KeyboardInterrupt , и он не может прервать работу кода в расширениях, если он явно не проверяет наличие
PyErr_CheckSignals()
, см. Cython, Python и KeyboardInterrupt игнорируютсяЯ бы избежал спящего потока более секунды, в любом случае - это эон времени процессора.
] Как мне вызвать функцию или как ее обернуть, чтобы, если она занимает больше 5 секунд, скрипт отменяет ее и делает Что-то еще?
Чтобы поймать его и сделать что-то еще, вы можете поймать KeyboardInterrupt.
>>> try: ... countdown(10) ... except KeyboardInterrupt: ... print('do something else') ... countdown started 10, 9, 8, 7, 6, countdown took too long do something else
Это в точности мое требование, и я использовал маршруты типа
$route['logued/presse-access'] = "logued/presse_access";
В моем предыдущем проекте мне нужно было создать 300-400 правил маршрутизации, большинство из них связано с преобразованием черточки в подчеркивание.
В моем следующем проекте я очень хочу избежать этого. Я сделал несколько быстрых взломов и протестировал его, хотя не использовал ни на одном живом сервере, он работает для меня. Сделайте следующее ..
Убедитесь, что префикс subclass_prefix имеет следующий вид в вашей системе / application / config / config.php
$config['subclass_prefix'] = 'MY_';
Затем загрузите файл с именем MY_Router.php в каталог system / application / libraries.
<?php
class MY_Router extends CI_Router {
function set_class($class)
{
//$this->class = $class;
$this->class = str_replace('-', '_', $class);
//echo 'class:'.$this->class;
}
function set_method($method)
{
// $this->method = $method;
$this->method = str_replace('-', '_', $method);
}
function _validate_request($segments)
{
// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.str_replace('-', '_', $segments[0]).EXT))
{
return $segments;
}
// Is the controller in a sub-folder?
if (is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
if (count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().str_replace('-', '_', $segments[0]).EXT))
{
show_404($this->fetch_directory().$segments[0]);
}
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
// Does the default controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.EXT))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// Can't find the requested controller...
show_404($segments[0]);
}
}
Теперь вы можете свободно использовать URL, например http://example.com/logued/presse-access , и он будет вызывать соответствующий контроллер и функцию, автоматически преобразовывая тире в подчеркивание.
Редактировать: Вот наше решение Codeigniter 2, которое отменяет новые функции CI_Router:
<?php
class MY_Router extends CI_Router {
function set_class($class)
{
$this->class = str_replace('-', '_', $class);
}
function set_method($method)
{
$this->method = str_replace('-', '_', $method);
}
function set_directory($dir) {
$this->directory = $dir.'/';
}
function _validate_request($segments)
{
if (count($segments) == 0)
{
return $segments;
}
// Does the requested controller exist in the root folder?
if (file_exists(APPPATH.'controllers/'.str_replace('-', '_', $segments[0]).'.php'))
{
return $segments;
}
// Is the controller in a sub-folder?
if (is_dir(APPPATH.'controllers/'.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($segments[0]);
$segments = array_slice($segments, 1);
while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($this->directory . $segments[0]);
$segments = array_slice($segments, 1);
}
if (count($segments) > 0)
{
// Does the requested controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().str_replace('-', '_', $segments[0]).'.php'))
{
if ( ! empty($this->routes['404_override']))
{
$x = explode('/', $this->routes['404_override']);
$this->set_directory('');
$this->set_class($x[0]);
$this->set_method(isset($x[1]) ? $x[1] : 'index');
return $x;
}
else
{
show_404($this->fetch_directory().$segments[0]);
}
}
}
else
{
// Is the method being specified in the route?
if (strpos($this->default_controller, '/') !== FALSE)
{
$x = explode('/', $this->default_controller);
$this->set_class($x[0]);
$this->set_method($x[1]);
}
else
{
$this->set_class($this->default_controller);
$this->set_method('index');
}
// Does the default controller exist in the sub-folder?
if ( ! file_exists(APPPATH.'controllers/'.$this->fetch_directory().$this->default_controller.'.php'))
{
$this->directory = '';
return array();
}
}
return $segments;
}
// If we've gotten this far it means that the URI does not correlate to a valid
// controller class. We will now see if there is an override
if ( ! empty($this->routes['404_override']))
{
$x = explode('/', $this->routes['404_override']);
$this->set_class($x[0]);
$this->set_method(isset($x[1]) ? $x[1] : 'index');
return $x;
}
// Nothing else to do at this point but show a 404
show_404($segments[0]);
}
}
Теперь нужно поместить этот файл, например application / core / MY_Router.php, и убедиться, что у него есть subclass_prefix определяется как $ config ['subclass_prefix'] = 'MY _';
в application / config / config.php
Несколько дополнительных строк кода было добавлено в метод _validate_request ()
:
while(count($segments) > 0 && is_dir(APPPATH.'controllers/'.$this->directory.$segments[0]))
{
// Set the directory and remove it from the segment array
$this->set_directory($this->directory . $segments[0]);
$segments = array_slice($segments, 1);
}
Он используется для того, чтобы можно было использовать многоуровневый подкаталог внутри каталога контроллеров, тогда как обычно мы можем использовать одноуровневый подкаталог внутри папки контроллеров и вызывать его по URL-адресу. Этот код можно удалить, если в нем нет необходимости, но это не повредит нормальному потоку.
Я не уверен, что вы могли бы сделать это с помощью маршрута ...
Однако где-то в основных библиотеках Codeigniter (возможно, Router или URI) будет что-то, что преобразует подчеркнутые uris в имя класса camelcase.
Я быстро просмотрел и не нашел, но если вы это сделаете, просто скопируйте эту библиотеку в папку вашего приложения / библиотек и измените ее там.
Я полагаю, что вы ищете либо предварительную систему, либо предварительный контроллер hook, который будет принимать запрошенный URI и обновлять его.
<?php
class MY_Router extends CI_Router
{
function _set_request($segments = array()) {
parent::_set_request(str_replace('-', '_', $segments));
}
}
?>
Поместите этот файл MY_Router.php в / application / libraries (CI1) или / application / core (CI2) Помните, что это повлияет на все сегменты, а не только на модуль, контроллер и метод.
Альтернативой этому расширению является добавление каждого сегмента в router.php. $ route ['this-is-a-module-or-controler'] = 'this_is_a_module_or_controller';
Как видите, метод расширения было бы проще использовать. Вы можете сделать так, чтобы функция также обрабатывала только первые два или три сегмента, чтобы на другие сегменты не повлияла замена _.