Feeds

Kushal Das: Trouble with signing and notarization on macOS for Tumpa

Planet Python - Fri, 2022-01-07 04:10

This week I released the first version of Tumpa on Mac. Though the actual changes required for building the Mac app and dmg file were small, but I had to reap apart those few remaining hairs on my head to get it working on any other Mac (than the building box). It was the classic case of Works on my laptop.

The issue

Tumpa is a Python application which uses PySide2 and also Johnnycanencrypt which is written in Rust.

I tried both briefcase tool and manual calling to codesign and create-dmg tools to create the tumpa.app and the tumpa-0.1.3.dmg.

After creating the dmg file, I had to submit it for notarisation to Apple, following:

xcrun /Applications/Xcode.app/Contents/Developer/usr/bin/altool --notarize-app --primary-bundle-id "in.kushaldas.Tumpa" -u "kushaldas@gmail.com" -p "@keychain:MYNOTARIZATION" -f macOS/tumpa-0.1.3.dmg

This worked successfully, after a few minutes I can see that the job passed. So, I can then staple the ticket on the dmg file.

xcrun stapler staple macOS/tumpa-0.1.3.dmg

I can install from the file, and run the application, sounds great.

But, whenever someone else tried to run the application after installing from dmg, it showed the following.

Solution

It took me over 4 hours to keep trying all possible combinations, and finally I had to pass --options=runtime,library to the codesign tool, and that did the trick. Not being able to figure out how to get more logs on Mac was making my life difficult.

I had to patch briefcase to make sure I can keep using it (also created the upstream issue).

--- .venv/lib/python3.9/site-packages/briefcase/platforms/macOS/__init__.py 2022-01-07 08:48:12.000000000 +0100 +++ /tmp/__init__.py 2022-01-07 08:47:54.000000000 +0100 @@ -117,7 +117,7 @@ '--deep', str(path), '--force', '--timestamp', - '--options', 'runtime', + '--options', 'runtime,library', ], check=True, )

You can see my build script, which is based on input from Micah.

I want to thank all of my new friends inside of SUNET who were excellent helping hands to test the multiple builds of Tumpa. Later many folks from IRC also jumped in to help to test the tool.

Categories: FLOSS Project Planets

Agiledrop.com Blog: Top Drupal blog posts from December 2021

Planet Drupal - Fri, 2022-01-07 03:00

Happy New Year, everyone! Let’s kick it off by revisiting some of the top Drupal blog posts written in the final month of 2021.

READ MORE
Categories: FLOSS Project Planets

Matt Glaman: Documenting PHPStan + Drupal!

Planet Drupal - Thu, 2022-01-06 23:54

In the early days of Drupal 9, most folks were only using PHPStan on their Drupal sites via drupal-check for deprecation checks. I am excited to see more folks using PHPStan directly with phpstan-drupal to perform static analysis and deprecation checks. However, folks are starting to hit some of the DrupalWTF's when performing static analysis on a magical code base

MAGIC?! Yes, Drupal has a lot of magical return types that are not typed. Inside phpstan-drupal, we try to help out as much as possible, but we cannot know everything.

Take a typical implementation of hook_ENTITY_TYPE_insert.

Categories: FLOSS Project Planets

Armin Ronacher: Rust Any Part 2: As-Any Hack

Planet Python - Thu, 2022-01-06 19:00

Yesterday I wrote about how to use the Any type in Rust to implement extension maps. One thing that was brought up as a response is that it's hard to implement Debug for the extension map as Any does not implement Debug. The challenge is that unfortunately in Rust you cannot do Box<dyn Any + Debug>. However there are ways around it.

The Simplifed Problem

Let's say you want to have a wrapper around a boxed any that you can debug. This is what we basically want to accomplish:

#[derive(Debug)] struct AnyBox(Box<dyn Any + Debug>);

If we were to compile this, the compiler doesn't come back happy:

error[E0225]: only auto traits can be used as additional traits in a trait object --> src/main.rs:9:29 | 9 | struct AnyBox(Box<dyn Any + Debug>); | --- ^^^^^ additional non-auto trait | | | first non-auto trait | = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Any + Debug {}` Supertraits

Thankfully the compiler actually tells is what the solution is: we need to create a new super trait that we can use. However it leaves us a bit in the dark of how to do this successfully. Basically we want to implement our super trait for all types implementing the individual traits. Something like this:

#[derive(Debug)] struct AnyBox(Box<dyn Any + Debug>); trait DebugAny: Any + Debug {} impl<T: Any + Debug + 'static> DebugAny for T {}

This will in fact compile and you will be able to construct such a box, but what will not work is downcasting:

fn main() { let any_box = AnyBox(Box::new(42i32)); dbg!(any_box.0.downcast_ref::<i32>()); }

The compiler will tell us that our value in the anybox has no method named downcast_ref:

error[E0599]: no method named `downcast_ref` found for struct `Box<(dyn DebugAny + 'static)>` in the current scope --> src/main.rs:15:20 | 15 | dbg!(any_box.0.downcast_ref::<i32>()); | ^^^^^^^^^^^^ method not found in `Box<(dyn DebugAny + 'static)>`

The reason for this is that a Box<dyn DebugAny> unfortunately is not a Box<dyn Any> and as such we don't get the methods from it that we need. So how do we fix this? The simplest method is the “as any” pattern where we implement a method on our DebugAny trait that upcasts into an Any. This looks like this:

trait DebugAny: Any + Debug { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; } impl<T: Any + Debug + 'static> DebugAny for T { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } }

Now we still can't downcast_ref on the box, but we can take our value, call as_any on it, retrieve a &dyn Any and then go to town:

fn main() { let any_box = AnyBox(Box::new(42i32)); dbg!(any_box.0.as_any().downcast_ref::<i32>()); dbg!(&any_box); }

Except if we run it, we get None. What's going on?

[src/main.rs:23] any_box.0.as_any().downcast_ref::<i32>() = None

The answer to this riddle has to do with how the method resolution works and blanket implementations. When we invoke as_any on Box<dyn DebugAny> we're not looking through the box, we're in fact invoking as_any on the Box<dyn DebugAny> itself since the box also implements our DebugAny now. So how do we reach through the box? By dereferencing it.

fn main() { let any_box = AnyBox(Box::new(42i32)); dbg!((*any_box.0).as_any().downcast_ref::<i32>()); dbg!(&any_box); }

And now we get what we expect:

[src/main.rs:23] (*any_box.0).as_any().downcast_ref::<i32>() = Some( 42, ) [src/main.rs:24] &any_box = AnyBox( 42, ) Debuggable Extension Map

These learnings we can now take back to building an extension map which can be debug printed. Let's take the non sync extension map from last time and modify it so we can debug print it:

use std::any::{Any, TypeId}; use std::cell::{Ref, RefCell, RefMut}; use std::collections::HashMap; use std::fmt::Debug; trait DebugAny: Any + Debug { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; } impl<T: Any + Debug + 'static> DebugAny for T { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } } #[derive(Default, Debug)] pub struct Extensions { map: RefCell<HashMap<TypeId, Box<dyn DebugAny>>>, } impl Extensions { pub fn insert<T: Debug + 'static>(&self, value: T) { self.map .borrow_mut() .insert(TypeId::of::<T>(), Box::new(value)); } pub fn get<T: Default + Debug + 'static>(&self) -> Ref<'_, T> { self.ensure::<T>(); Ref::map(self.map.borrow(), |m| { m.get(&TypeId::of::<T>()) .and_then(|b| (**b).as_any().downcast_ref()) .unwrap() }) } pub fn get_mut<T: Default + Debug + 'static>(&self) -> RefMut<'_, T> { self.ensure::<T>(); RefMut::map(self.map.borrow_mut(), |m| { m.get_mut(&TypeId::of::<T>()) .and_then(|b| (**b).as_any_mut().downcast_mut()) .unwrap() }) } fn ensure<T: Default + Debug + 'static>(&self) { if self.map.borrow().get(&TypeId::of::<T>()).is_none() { self.insert(T::default()); } } }

Adding some stuff into the map and debug printing it makes it output something like this now:

[src/main.rs:63] &extensions = Extensions { map: RefCell { value: { TypeId { t: 13431306602944299956, }: 42, }, }, }

In this case I placed a 32bit integer 42 in the map and we can see that it prints out the type id of that as key, and 42 as value.

Retaining Type Names

If we want to retain the original type name and not just type ID we could change our TypeId key for a custom type which also stores the original type name. This could be accomplished by creating a wrapper for our TypeId which uses std::any::type_name internally:

use std::any::{TypeId, type_name}; use std::hash::{Hash, Hasher}; use std::fmt::{self, Debug}; pub struct TypeKey(TypeId, &'static str); impl TypeKey { pub fn of<T: 'static>() -> TypeKey { TypeKey(TypeId::of::<T>(), type_name::<T>()) } } impl Hash for TypeKey { fn hash<H: Hasher>(&self, state: &mut H) { self.0.hash(state); } } impl PartialEq for TypeKey { fn eq(&self, other: &Self) -> bool { self.0 == other.0 } } impl Eq for TypeKey {} impl Debug for TypeKey { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.1) } }

Now we can replace our use of TypeId with TypeKey in the extension map and our debug output looks like this instead:

[src/main.rs:90] &extensions = Extensions { map: RefCell { value: { i32: 42, alloc::vec::Vec<i32>: [ 1, 2, 3, ], }, }, }

Note that i additionally inserted a Vec<i32> into the map to get some more extra output.

Categories: FLOSS Project Planets

Reproducible Builds (diffoscope): diffoscope 199 released

Planet Debian - Thu, 2022-01-06 19:00

The diffoscope maintainers are pleased to announce the release of diffoscope version 199. This version includes the following changes:

[ Chris Lamb ] * Support both variants of "odt2txt", including the one provided by unoconv. (Closes: reproducible-builds/diffoscope#298) [ Jelle van der Waa ] * Add external tool reference on Arch Linux for xb-tool.

You find out more by visiting the project homepage.

Categories: FLOSS Project Planets

QCoro 0.5.0 Release Announcement

Planet KDE - Thu, 2022-01-06 14:00

It took a few months, but there’s a new release of QCoro with some new cool features. This change contains a breaking change in CMake, wich requires QCoro users to adjust their CMakeLists.txt. I sincerely hope this is the last breaking change for a very long time.

Major highlights in this release:

  • Co-installability of Qt5 and Qt6 builds of QCoro
  • Complete re-work of CMake configuration
  • Support for compiling QCoro with Clang against libstdc++
Co-installability of Qt5 and Qt6 builds of QCoro

This change mostly affects packagers of QCoro. It is now possible to install both Qt5 and Qt6 versions of QCoro alongside each other without conflicting files. The shared libraries now contain the Qt version number in their name (e.g. libQCoro6Core.so) and header files are also located in dedicated subdirectories (e.g. /usr/include/qcoro6/{qcoro,QCoro}). User of QCoro should not need to do any changes to their codebase.

Complete re-work of CMake configuration

This change affects users of QCoro, as they will need to adjust CMakeLists.txt of their projects. First, depending on whether they want to use Qt5 or Qt6 version of QCoro, a different package must be used. Additionally, list of QCoro components to use must be specified:

find_package(QCoro5 REQUIRED COMPONENTS Core Network DBus)

Finally, the target names to use in target_link_libraries have changed as well:

  • QCoro::Core
  • QCoro::Network
  • QCoro::DBus

The version-less QCoro namespace can be used regardless of whether using Qt5 or Qt6 build of QCoro. QCoro5 and QCoro6 namespaces are available as well, in case users need to combine both Qt5 and Qt6 versions in their codebase.

This change brings QCoro CMake configuration system to the same style and behavior as Qt itself, so it should now be easier to use QCoro, especially when supporting both Qt5 and Qt6.

Support for compiling QCoro with Clang against libstdc++

Until now, when the Clang compiler was detected, QCoro forced usage of LLVM’s libc++ standard library. Coroutine support requires tight co-operation between the compiler and standard library. Because Clang still considers their coroutine support experimental it expects all coroutine-related types in standard library to be located in std::experimental namespace. In GNU’s libstdc++, coroutines are fully supported and thus implemented in the std namespace. This requires a little bit of extra glue, which is now in place.

Full changelog
  • QCoro can now be built with Clang against libstdc++ (#38, #22)
  • Qt5 and Qt6 builds of QCoro are now co-installable (#36, #37)
  • Fixed early co_return not resuming the caller (#24, #35)
  • Fixed QProcess example (#34)
  • Test suite has been improved and extended (#29, #31)
  • Task move assignment operator checks for self-assignment (#27)
  • QCoro can now be built as a subdirectory inside another CMake project (#25)
  • Fixed QCoroCore/qcorocore.h header (#23)
  • DBus is disabled by default on Windows, Mac and Android (#21)

Thanks to everyone who contributed to QCoro!

Download

You can download QCoro 0.4.0 here or check the latest sources on QCoro GitHub.

More About QCoro

If you are interested in learning more about QCoro, go read the documentation, look at the [first release announcement][dvratil-010-announcement], which contains a nice explanation and example or watch recording of my talk about C++20 coroutines and QCoro this years’ Akademy.

Categories: FLOSS Project Planets

Python for Beginners: Recursion In Python

Planet Python - Thu, 2022-01-06 09:10

You might have studied functions in python. You might also have used for loops and while loops to perform a task repetitively while programming in Python. In this article, we will discuss recursion and recursive functions in Python.

What Is Recursion?

Recursion is a mathematical concept in which we define something in terms of itself. 

For instance, we can define the sum of the first ten natural numbers as the sum of the first nine natural numbers added to the tenth number. 

Similarly, we can define the sum of the first nine natural numbers as the sum of the first eight natural numbers added to the ninth natural number.

Here, you can see that we are breaking the problem of the first ten natural numbers into smaller problems like finding the sum of the first 9 numbers, then finding the sum of the first 8 numbers, and so on. In this way, we will come to a position where we have to find the sum of the first natural number i.e. 1 itself. After that, we will have to perform only the atomic addition instead of worrying about the count of natural numbers we are finding the sum of. 

When To Use Recursion In Python?

As seen above, we can use recursion whenever we can break a problem into a similar but smaller problem.  The most common problems where we use recursion are:

  1. Binary tree traversal problems
  2. Finding the factorial of a number
  3. Tower of Hanoi problem
  4. Finding Fibonacci series
How To Use Recursion In Python?

In programming, if a function calls itself, we say that it is a recursive function i.e. it works on the concept of recursion. You can use recursion in python to implement the solution for any problem that can be reduced to a similar but smaller problem. 

For instance, let us try to find the sum of the first 10 natural numbers. For that, let us define a function sumOfNumbers() that receives an input number N and returns the sum of numbers from 1 to N.

  • To calculate the sum of first 10 natural numbers i.e.  sumOfNumbers(10), we will find the sum of the first 9 natural numbers i.e. sumOfNumbers(9) and will add 10 to it.
  • Similarly, to find the sum of first 9 natural numbers i.e.  sumOfNumbers(9), we will find the sum of the first 8 natural numbers i.e. sumOfNumbers(8) and will add 9 to it.
  • Again, to find the sum of the first 8 natural numbers i.e.  sumOfNumbers(8), we will find the sum of the first 7 natural numbers i.e. sumOfNumbers(7) and will add 8 to it.
  • After that, to find the sum of the first 7 natural numbers i.e.  sumOfNumbers(7), we will find the sum of the first 6 natural numbers i.e. sumOfNumbers(6) and will add 7 to it.
  • In this way, we will reach a position when we will have to calculate the sum of the first natural number i.e. sumOfNumbers(1). Here, we can simply return 1. This is also called the base case of the recursion as the problem cannot be reduced further into smaller problems. 

We can implement the above algorithm as follows.

def sumOfNumbers(N): if N == 1: return N else: return N + sumOfNumbers(N - 1) input_number = 10 output = sumOfNumbers(input_number) print("Sum of first {} natural numbers is {}".format(input_number, output)) input_number = 20 output = sumOfNumbers(input_number) print("Sum of first {} natural numbers is {}".format(input_number, output))

Output:

Sum of first 10 natural numbers is 55 Sum of first 20 natural numbers is 210

While using recursion, we must specify the base case. Otherwise, the program will continue its execution continuously and run into RecursionError. This is due to the fact that the maximum number of recursive calls a function can make is capped at 1000 in Python. If any function makes more than 1000 recursive calls, a RecursionError exception will occur.

Conclusion

In this article, we have discussed recursion in python. We have also implemented a program to find the sum of the first 10 natural numbers in Python. To learn more about functions, you can read this article on closures in python.

The post Recursion In Python appeared first on PythonForBeginners.com.

Categories: FLOSS Project Planets

Python GUIs: Drag &amp; drop widgets with PyQt — Sort widgets visually with drag and drop in a container

Planet Python - Thu, 2022-01-06 08:00

This week I had an interesting question from a reader of my PyQt6 book, about how to handle dragging and dropping of widgets in a container showing the dragged widget as it is moved.

I'm interested in managing movement of a QWidget with mouse in a container. I've implemented the application with drag & drop, exchanging the position of buttons, but I want to show the motion of QPushButton, like what you see in Qt Designer. Dragging a widget should show the widget itself, not just the mouse pointer.

First, we'll implement the simple case which drags widgets without showing anything extra. Then we can extend it to answer the question.

Drag & drop widgets

We'll start with this simple application which creates a window using QWidget and places a series of QPushButton widgets into it.

You can substitute QPushButton for any other widget you like, e.g. QLabel

python from PyQt5.QtWidgets import QApplication, QHBoxLayout, QWidget, QPushButton class Window(QWidget): def __init__(self): super().__init__() self.blayout = QHBoxLayout() for l in ['A', 'B', 'C', 'D']: btn = QPushButton(l) self.blayout.addWidget(btn) self.setLayout(self.blayout) app = QApplication([]) w = Window() w.show() app.exec_()

If you run this you should see something like this.

The series of QPushButton widgets in a horizontal layout.

Here we're creating a window, but the Window widget is subclassed from QWidget, meaning you can add this widget to any other layout. See later for an example of a generic object sorting widget.

QPushButton objects aren't usually draggable, so to handle the mouse movements and initiate a drag we need to implement a subclass. We can add the following to the top of the file.

python from PyQt5.QtCore import Qt, QMimeData from PyQt5.QtGui import QDrag class DragButton(QPushButton): def mouseMoveEvent(self, e): if e.buttons() == Qt.LeftButton: drag = QDrag(self) mime = QMimeData() drag.setMimeData(mime) drag.exec_(Qt.MoveAction)

We implement a mouseMoveEvent which accepts the single e parameter of the event. We check to see if the left mouse button is pressed on this event -- as it would be when dragging -- and then initiate a drag. To start a drag, we create a QDrag object, passing in self to give us access later to the widget that was dragged. We also must pass in mime data. This is used for including information about what is dragged, particularly for passing data between applications. However, as here, it is fine to leave this empty.

Finally, we initiate a drag by calling drag.exec_(Qt.MoveAction). As with dialogs exec_() starts a new event loop, blocking the main loop until the drag is complete. The parameter Qt.MoveAction tells the drag handler what type of operation is happening, so it can show the appropriate icon tip to the user.

You can update the main window code to use our new DragButton class as follows.

python class Window(QWidget): def __init__(self): super().__init__() self.blayout = QHBoxLayout() for l in ['A', 'B', 'C', 'D']: btn = DragButton(l) self.blayout.addWidget(btn) self.setLayout(self.blayout)

If you run the code now, you can drag the buttons, but you'll notice the drag is forbidden.

Dragging of the widget starts but is forbidden.

What's happening? The mouse movement is being detected by our DragButton object and the drag started, but the main window does not accept drag & drop.

To fix this we need to enable drops on the window and implement dragEnterEvent to actually accept them.

python class Window(QWidget): def __init__(self): super().__init__() self.setAcceptDrops(True) self.blayout = QHBoxLayout() for l in ['A', 'B', 'C', 'D']: btn = DragButton(l) self.blayout.addWidget(btn) self.setLayout(self.blayout) def dragEnterEvent(self, e): e.accept()

If you run this now, you'll see the drag is now accepted and you see the move icon. This indicates that the drag has started and been accepted by the window we're dragging onto. The icon shown is determined by the action we pass when calling drag.exec_().

Dragging of the widget starts and is accepted, showing a move icon.

Releasing the mouse button during a drag drop operation triggers a dropEvent on the widget you're currently hovering the mouse over (if it is configured to accept drops). In our case that's the window. To handle the move we need to implement the code to do this in our dropEvent method.

The drop event contains the position the mouse was at when the button was released & the drop triggered. We can use this to determine where to move the widget to.

python def dropEvent(self, e): pos = e.pos() widget = e.source() for n in range(self.blayout.count()): # Get the widget at each index in turn. w = self.blayout.itemAt(n).widget() if pos.x() < w.x(): # We didn't drag past this widget. # insert to the left of it. self.blayout.insertWidget(n-1, widget) break e.accept()

To determine where to place the widget, we iterate over all the widgets in the layout, until we find one who's x position is greater than that of the mouse pointer. If so then when insert the widget directly to the left of this widget and exit the loop.

The effect of this is that if you drag 1 pixel past the start of another widget, which might be a bit confusing. You adjust this the cut off to use the middle of the widget using if pos.x() < w.x() + w.size().width() // 2: -- that is x + half of the width.

python def dropEvent(self, e): pos = e.pos() widget = e.source() for n in range(self.blayout.count()): # Get the widget at each index in turn. w = self.blayout.itemAt(n).widget() if pos.x() < w.x() + w.size().width() // 2: # We didn't drag past this widget. # insert to the left of it. self.blayout.insertWidget(n-1, widget) break e.accept()

The complete working drag-drop code is shown below.

python from PyQt5.QtWidgets import QApplication, QHBoxLayout, QWidget, QPushButton from PyQt5.QtCore import Qt, QMimeData from PyQt5.QtGui import QDrag class DragButton(QPushButton): def mouseMoveEvent(self, e): if e.buttons() == Qt.LeftButton: drag = QDrag(self) mime = QMimeData() drag.setMimeData(mime) drag.exec_(Qt.MoveAction) class Window(QWidget): def __init__(self): super().__init__() self.setAcceptDrops(True) self.blayout = QHBoxLayout() for l in ['A', 'B', 'C', 'D']: btn = DragButton(l) self.blayout.addWidget(btn) self.setLayout(self.blayout) def dragEnterEvent(self, e): e.accept() def dropEvent(self, e): pos = e.pos() widget = e.source() for n in range(self.blayout.count()): # Get the widget at each index in turn. w = self.blayout.itemAt(n).widget() if pos.x() < w.x() + w.size().width() // 2: # We didn't drag past this widget. # insert to the left of it. self.blayout.insertWidget(n-1, widget) break e.accept() app = QApplication([]) w = Window() w.show() app.exec_() Visual drag & drop

So now we have our working drag & drop implementation we can move on to showing the drag visually. What we want to achieve here is showing the button being dragged next to the mouse point as it is dragged.

Qt's QDrag handler natively provides a mechanism for showing dragged objects which we can use. We can update our DragButton class to pass a pixmap image to QDrag and this will be displayed under the mouse pointer as the drag occurs. To show the widget, we just need to get a QPixmap of the widget we're dragging.

python from PyQt5.QtWidgets import QApplication, QHBoxLayout, QWidget, QPushButton from PyQt5.QtCore import Qt, QMimeData from PyQt5.QtGui import QDrag, QPixmap class DragButton(QPushButton): def mouseMoveEvent(self, e): if e.buttons() == Qt.LeftButton: drag = QDrag(self) mime = QMimeData() drag.setMimeData(mime) pixmap = QPixmap(self.size()) self.render(pixmap) drag.setPixmap(pixmap) drag.exec_(Qt.MoveAction)

To create the pixmap we create a QPixmap object passing in the size of the widget this event is fired on with self.size(). This creates an empty pixmap which we can then pass into self.render to render -- or draw -- the current widget onto it. That's it. Then we set the resulting pixmap on the drag object.

If you run the code with this modification you'll see something like the following --

Dragging of the widget showing the dragged widget.

Generic drag & drop container

We can take this a step further and implement a generic drag drop widget which allows us to sort arbitrary objects. In the code below we've created a new widget DragWidget which can be added to any window. You can add items -- instances of DragItem -- which you want to be sorted, as well as setting data on them. When items are re-ordered the new order is emitted as a signal orderChanged.

python from PyQt5.QtWidgets import QApplication, QHBoxLayout, QWidget, QLabel, QMainWindow, QVBoxLayout from PyQt5.QtCore import Qt, QMimeData, pyqtSignal from PyQt5.QtGui import QDrag, QPixmap class DragItem(QLabel): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.setContentsMargins(25, 5, 25, 5) self.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setStyleSheet("border: 1px solid black;") # Store data separately from display label, but use label for default. self.data = self.text() def set_data(self, data): self.data = data def mouseMoveEvent(self, e): if e.buttons() == Qt.LeftButton: drag = QDrag(self) mime = QMimeData() drag.setMimeData(mime) pixmap = QPixmap(self.size()) self.render(pixmap) drag.setPixmap(pixmap) drag.exec_(Qt.MoveAction) class DragWidget(QWidget): """ Generic list sorting handler. """ orderChanged = pyqtSignal(list) def __init__(self, *args, orientation=Qt.Orientation.Vertical, **kwargs): super().__init__() self.setAcceptDrops(True) # Store the orientation for drag checks later. self.orientation = orientation if self.orientation == Qt.Orientation.Vertical: self.blayout = QVBoxLayout() else: self.blayout = QHBoxLayout() self.setLayout(self.blayout) def dragEnterEvent(self, e): e.accept() def dropEvent(self, e): pos = e.pos() widget = e.source() for n in range(self.blayout.count()): # Get the widget at each index in turn. w = self.blayout.itemAt(n).widget() if self.orientation == Qt.Orientation.Vertical: # Drag drop vertically. drop_here = pos.y() < w.y() + w.size().height() // 2 else: # Drag drop horizontally. drop_here = pos.x() < w.x() + w.size().width() // 2 if drop_here: # We didn't drag past this widget. # insert to the left of it. self.blayout.insertWidget(n-1, widget) self.orderChanged.emit(self.get_item_data()) break e.accept() def add_item(self, item): self.blayout.addWidget(item) def get_item_data(self): data = [] for n in range(self.blayout.count()): # Get the widget at each index in turn. w = self.blayout.itemAt(n).widget() data.append(w.data) return data class MainWindow(QMainWindow): def __init__(self): super().__init__() self.drag = DragWidget(orientation=Qt.Orientation.Vertical) for n, l in enumerate(['A', 'B', 'C', 'D']): item = DragItem(l) item.set_data(n) # Store the data. self.drag.add_item(item) # Print out the changed order. self.drag.orderChanged.connect(print) container = QWidget() layout = QVBoxLayout() layout.addStretch(1) layout.addWidget(self.drag) layout.addStretch(1) container.setLayout(layout) self.setCentralWidget(container) app = QApplication([]) w = MainWindow() w.show() app.exec_()

Generic drag-drop sorting in horizontal orientation.

You'll notice that when creating the item, you can set the label by passing it in as a parameter (just like for a normal QLabel which we've subclassed from). But you can also set a data value, which is the internal value of this item -- this is what will be emitted when the order changes, or if you call get_item_data yourself. This separates the visual representation from what is actually being sorted, meaning you can use this to sort anything not just strings.

In the example above we're passing in the enumerated index as the data, so dragging will output (via the print connected to orderChanged) something like:

python [1, 0, 2, 3] [1, 2, 0, 3] [1, 0, 2, 3] [1, 2, 0, 3]

If you remove the item.set_data(n) you'll see the labels emitted on changes.

python ['B', 'A', 'C', 'D'] ['B', 'C', 'A', 'D']

We've also implemented orientation onto the DragWidget using the Qt built in flags Qt.Orientation.Vertical or Qt.Orientation.Horizontal. This setting this allows you sort items either vertically or horizontally -- the calculations are handled for both directions.

Generic drag-drop sorting in vertical orientation.

For more, see the complete PyQt6 tutorial.

Categories: FLOSS Project Planets

KDE News & Merge Requests + WE NEED YOU!

Planet KDE - Thu, 2022-01-06 05:44
💸💸 Help me contribute to KDE and do these videos: 💸💸 Patreon: https://www.patreon.com/niccolove Youtube: https://www.youtube.com/channel/UCONH73CdRXUjlh3-DdLGCPw/join Paypal: https://paypal.me/niccolove Stay in the loop: https://t.me/veggeroblog My website is https://niccolo.venerandi.com and if you want to contact me, my telegram handle is [at] veggero.
Categories: FLOSS Project Planets

Introducing KDBindings

Planet KDE - Thu, 2022-01-06 05:00

All Qt developers should know about signals, slots, and properties. Those of you who have used QML will know that property bindings are super useful and cool. Bindings allow us to write more reactive and declarative style code. However, they are only available within QML, which means there are no compile time errors when you do something wrong.

Wouldn’t it be nice if we could have the sunlit uplands of Brexit — erm, I mean bindings — from the comfort and speed of C++? Wouldn’t it be nice if you could use this with or without Qt? Wouldn’t it be nice if this was just plain C++ with no moc required? Wouldn’t it be nice if you could avoid executing a binding many times when you update each dependent property (lazy/deferred evaluation)? Wouldn’t it be nice if you could have this right now?

Well, you can! Read on to find out more details. For those of you who can’t wait, you can grab the code and start using it right now from GitHub.

How C++ Bindings Look in Use

Here is a simple but complete example to give you a taste of what using properties and bindings in C++ looks like:

#include "kdbindings/binding.h" #include "kdbindings/property.h" #include "kdbindings/signal.h" #include <iostream> using namespace KDBindings; class Image { public: const int bytesPerPixel = 4; Property<int> width { 800 }; Property<int> height { 600 }; const Property<int> byteSize = makeBoundProperty(bytesPerPixel * width * height); }; int main() { Image img; std::cout << "The initial size of the image = " << img.byteSize.get() << " bytes" << std::endl; img.byteSize.valueChanged().connect([] (const int &newValue) { std::cout << "The new size of the image = " << newValue << " bytes" << std::endl; }); img.width = 1920; img.height = 1080; return 0; }

The Image class declares 3 properties, all containing an integer. The first two, width and height, are simply initialized to values. The third property, byteSize, is initialized to whatever is returned from the makeBinding() function. We will go into this in full and gory detail later. This, however, is how we create an immediate-mode property binding — that is, a binding that gets evaluated immediately when any of it’s dependent properties change. In this case, that means if width or height changes, then byteSize will get a new value. Pretty easy and natural, isn’t it? And we don’t have to worry about the mechanics of connecting to changed signals; that is all taken care of for us.

The main function simply instantiates an Image object’s printing out the initial value of the byteSize property. We use the get() function to extract the contained value here but we do provide an overloaded stream operator to do this for us too.

On line 24, we create a lambda “slot” and connect it to the valueChanged() signal of the byteSize property so that we print out the new image size whenever it changes. This syntax, although slightly different from Qt’s, should be familiar enough to pick up and use. The reason we can do this here is because in this implementation a signal is itself an object rather than just a function on a QObject.

Lines 28 and 29 both alter the other properties of the image. Each of these causes the binding expression to be re-evaluated. We will show later how to avoid this and have the bindings evaluated lazily. The complete output from running this simple example is:

The initial size of the image = 1920000 bytes The new size of the image = 4608000 bytes The new size of the image = 8294400 bytes Lazy Property Bindings

One of the issues with property bindings in Qt5/QML is that they are evaluated as soon as any of the properties on which they depend are changed. If you are updating a lot of properties, this can lead to a huge amount of wasted CPU time spent evaluating a binding that will be invalidated the next time you change a dependent property. Well, we can avoid this completely by introducing the concept of a BindingEvaluator.

To make use of lazily evaluated property bindings, all you need to do is to pass in a BindingEvaluator as the first argument to the makeBinding() function:

static BindingEvaluator evaluator; ... const Property<int> byteSize = makeBoundProperty(evaluator, bytesPerPixel * width * height);

With the binding associated to an evaluator, it is now lazily evaluated. No matter how many dependent input properties you change, the binding is only evaluated when you request it:

img.width = 1920; // Does not trigger an evaluation of the binding img.height = 1080; // Nor does this evaluator.evaluateAll(); // This does!

With the above changes to our example, the output now becomes:

The initial size of the image = 1920000 bytes The new size of the image = 8294400 bytes

And you can see we have saved the pointless evaluation of the binding when we change the image width knowing that we will then immediately change the image height, too.

This gives us a lot more control over bindings than is possible in QML and allows us to save precious CPU time/battery power!

It gets better, though. With the evaluator concept, we can go even further:

  • You could group bindings into logical sets based upon their purpose, e.g., all the bindings from subsystem A use this evaluator and all the bindings from subsystem B use a different evaluator. Triggering evaluator A before B means you can be sure that all the state in subsystem A is consistent before you move on to subsystem B’s bindings.
  • Building upon the first point, we can also control the frequency at which we update property bindings. For example, we may want all the bindings in the back-end data layer to be updated at full frequency (once per simulation loop). However, if you want to attach to your simulation a GUI that is not an unreadable strobe of flashing numbers, you could elect to update the bindings related to GUI code only once every 0.5s, say.

The choice is yours. This library only provides the facility; what you choose to do with this lazy evaluation is entirely up to you!

Actually, the immediate mode bindings also use an evaluator behind-the-scenes. It’s just a dummy one, though, and we ignore it. We just kick off an evaluation as soon as we see the expression is dirty, via a signal.

What We Can Do in a Property Binding

Thus far, we have only seen a trivial example of a property binding. This sufficed to show how to use bindings, but it’s not particularly compelling if all we can use is a multiply operation. So what else can we put into property bindings? Well, pretty much anything is the answer.

  • The usual unary operators (+, -, !, ~)
  • Binary operators (+, -, *, /, %, etc)
  • Normal values
  • Property values
  • Functions

This means you can do more interesting things inside your property bindings, such as:

BindingEvaluator evaluator; Property<int> a { 2 }; Property<int> b { 7 }; Property<int> c { 3 }; Property<int> d { 4 }; auto e = makeBoundProperty(evaluator, (a + b) * (c + d)); // e is a Property<int> too. Property<float> x { -7.5f }; Property<float> y { 2.35f }; auto z = makeBoundProperty(evaluator, y + abs(sin(x)));

“That’s cool, uh-huh. But I’ve got this other custom function I want to use in a binding…” No problem! It’s easy to expose your own functions for use with bindings. Here’s how:

struct node_paraboloid { template <typename T> auto operator()(T&& x, T&& y) const // Use as many arguments as you like { // Do something useful or call a library function... return x * x + y * y; } }; KDBINDINGS_DECLARE_FUNCTION(paraboloid, node_paraboloid {}) auto evaluator = BindingEvaluator{}; Property<int> x { -2 }; Property<int> y { 3 }; auto z = makeBoundProperty(evaluator, paraboloid(x, y));

Pretty neat, eh? And as the comments say, we can use any number of function arguments, thanks to the wonders of variadic templates. This means that we can put pretty much anything into a binding expression. It’s quite neat how this works behind-the-scenes with compile time building of the dependency tree — but I’ll save that for a deep-dive in a follow-up post.

Broken Bindings

Anyone who has used property bindings in QML will know just how much of a pain in the posterior broken bindings can be. Easy to do by mistake and can be annoyingly difficult to track down! Never fear! Thanks to C++ and a bit of design work, we can help eliminate the curse of broken bindings!

Compile-time Prevention

Just mark the property with a binding as const and this will stop you from assigning another binding or value to it at compile time.

Property<int> a { 10 }; Property<int> b { 14 }; const Property<int> x = makeBoundProperty(defaultEvaluator(), 2 * (a + b)); ... x = 35; // Compile-time error! Run-time prevention

If for whatever reason you can’t mark your property as const, e.g., if you want to switch it between two different bindings depending upon a mode, then you can still prevent accidentally assigning a value to the property:

Property<int> a { 10 }; Property<int> b { 14 }; Property<int> x = makeBoundProperty(defaultEvaluator(), 2 * (a + b)); ... x = 35; // Throws a ReadOnlyProperty exception

The KDBindings project allows us to have:

  • Signals and slots with support for missing/default arguments.
  • Properties templated on the contained type.
  • Use of signals and properties anywhere. No need for QObject or moc.
  • Property bindings allowing reactive code to be written without having to do all the low-level, error-prone plumbing by hand.
  • Plain C++! Yay for compile time errors, const correctness, more efficiency, debugging!
  • Lazy evaluation of property bindings!
  • No more broken bindings (hopefully)!
  • A totally stand-alone “header-only” library.
  • Compatibility with any C++17 compiler.
  • The option of use with Qt, if you so wish.
  • Available now and MIT licensed!

In a follow-up post, we will take a look at some of the interesting inner workings of KDBindings.

Please feel free to take it for a test-drive and let us know what you think.

About KDAB

If you like this article and want to read similar material, consider subscribing via our RSS feed.

Subscribe to KDAB TV for similar informative short video content.

KDAB provides market leading software consulting and development services and training in Qt, C++ and 3D/OpenGL. Contact us.

 

The post Introducing KDBindings appeared first on KDAB.

Categories: FLOSS Project Planets

GNU Guix: GNU Guix maintainer rotation

GNU Planet! - Thu, 2022-01-06 04:00

For some time already, Ludovic and Marius have voiced their desire to step down from the Guix maintainers collective. An email announcing the news and calling for new maintainers was sent more than 4 months ago. We're very happy to announce that Efraim Flashner has responded to the call and accepted to join the Guix maintainers collective! Efraim has been with Guix for a long time -- the first recorded Git history of their activity goes back to 2015, and they have since authored more than 6000 commits! More importantly, Efraim has demonstrated traits we value for a co-maintainer, such as good communication and collaboration abilities, values that align well with those of the GNU project and overall a person you'd like to hang out with at FOSDEM :-).

We're sad to see Ludovic and Marius step down from their current duties, but we take comfort knowing they will remain among us for the good hacks and occasional guidance. Loosing them as co-maintainer will be a difficult test for the remaining Guix co-maintainers. Ludovic's wit, relentless energy, tactful communication and wise planning have been invaluable to the Guix project throughout the years. Ludovic has mentioned this decision was at least partly based on their desire to guard Guix against the Founder's syndrome, which is something we can only applaud. Marius has served as co-maintainer since October 2019. Their calm, composed attitude has helped us navigating through at times difficult situations, and their technical wizardry has brought us the likes of ungoogled-chromium and a Ganeti service, among many others.

Let's take a moment to say thank you to Ludovic and Marius for their valuable years as maintainers, and wish they can enjoy their newfound extra time :-).

Let's also wish a warm welcome to Efraim. Thank you for stepping up to become a co-maintainer, Efraim!

The Guix co-maintainers

The Guix maintainer collective now consists of Efraim Flashner, Mathieu Othacehe, Maxim Cournoyer and Tobias Geerinckx-Rice. You can reach us all by email at guix-maintainers@gnu.org, a private alias.

For information about the responsibilities assumed by the Guix co-maintainers, you are encouraged to read a previous blog post that covered the topic.

About GNU Guix

GNU Guix is a transactional package manager and an advanced distribution of the GNU system that respects user freedom. Guix can be used on top of any system running the Hurd or the Linux kernel, or it can be used as a standalone operating system distribution for i686, x86_64, ARMv7, AArch64 and POWER9 machines.

In addition to standard package management features, Guix supports transactional upgrades and roll-backs, unprivileged package management, per-user profiles, and garbage collection. When used as a standalone GNU/Linux distribution, Guix offers a declarative, stateless approach to operating system configuration management. Guix is highly customizable and hackable through Guile programming interfaces and extensions to the Scheme language.

Categories: FLOSS Project Planets

KDE Gear 21.12.1

Planet KDE - Wed, 2022-01-05 19:00

Over 120 individual programs plus dozens of programmer libraries and feature plugins are released simultaneously as part of KDE Gear.

Today they all get new bugfix source releases with updated translations. Distro and app store packagers should update their application packages.

Categories: FLOSS Project Planets

Jacob Adams: Linux Hibernation Documentation

Planet Debian - Wed, 2022-01-05 19:00

Recently I’ve been curious about how hibernation works on Linux, as it’s an interesting interaction between hardware and software. There are some notes in the Arch wiki and the kernel documentation (as well as some kernel documentation on debugging hibernation and on sleep states more generally), and of course the ACPI Specification

The Formal Definition

ACPI (Advanced Configuration and Power Interface) is, according to the spec, “an architecture-independent power management and configuration framework that forms a subsystem within the host OS” which defines “a hardware register set to define power states.”

ACPI defines four global system states G0, working/on, G1, sleeping, G2, soft off, and G3, mechanical off1. Within G1 there are 4 sleep states, numbered S1 through S4. There are also S0 and S5, which are equivalent to G0 and G2 respectively2.

Sleep

According to the spec, the ACPI S1-S4 states all do the same thing from the operating system’s perspective, but each saves progressively more power, so the operating system is expected to pick the deepest of these states when entering sleep. However, most operating systems3 distinguish between S1-S3, which are typically referred to as sleep or suspend, and S4, which is typically referred to as hibernation.

S1: CPU Stop and Cache Wipe

The CPU caches are wiped and then the CPU is stopped, which the spec notes is equivalent to the WBINVD instruction followed by the STPCLK signal on x86. However, nothing is powered off.

S2: Processor Power off

The system stops the processor and most system clocks (except the real time clock), then powers off the processor. Upon waking, the processor will not continue what it was doing before, but instead use its reset vector4.

S3: Suspend/Sleep (Suspend-to-RAM)

Mostly equivalent to S2, but hardware ensures that only memory and whatever other hardware memory requires are powered.

S4: Hibernate (Suspend-to-Disk)

In this state, all hardware is completely powered off and an image of the system is written to disk, to be restored from upon reapplying power. Writing the system image to disk can be handled by the operating system if supported, or by the firmware.

Linux Sleep States

Linux has its own set of sleep states which mostly correspond with ACPI states.

Suspend-to-Idle

This is a software only sleep that puts all hardware into the lowest power state it can, suspends timekeeping, and freezes userspace processes.

All userspace and some kernel threads5, except those tagged with PF_NOFREEZE, are frozen before the system enters a sleep state. Frozen tasks are sent to the __refrigerator(), where they set TASK_UNINTERRUPTIBLE and PF_FROZEN and infinitely loop until PF_FROZEN is unset6.

This prevents these tasks from doing anything during the imaging process. Any userspace process running on a different CPU while the kernel is trying to create a memory image would cause havoc. This is also done because any filesystem changes made during this would be lost and could cause the filesystem and its related in-memory structures to become inconsistent. Also, creating a hibernation image requires about 50% of memory free, so no tasks should be allocating memory, which freezing also prevents.

Standby

This is equivalent to ACPI S1.

Suspend-to-RAM

This is equivalent to ACPI S3.

Hibernation

Hibernation is mostly equivalent to ACPI S4 but does not require S4, only requiring “low-level code for resuming the system to be present for the underlying CPU architecture” according to the Linux sleep state docs.

To hibernate, everything is stopped and the kernel takes a snapshot of memory. Then, the system writes out the memory image to disk. Finally, the system either enters S4 or turns off completely.

When the system restores power it boots a new kernel, which looks for a hibernation image and loads it into memory. It then overwrites itself with the hibernation image and jumps to a resume area of the original kernel7. The resumed kernel restores the system to its previous state and resumes all processes.

Hybrid Suspend

Hybrid suspend does not correspond to an official ACPI state, but instead is effectively a combination of S3 and S4. The system writes out a hibernation image, but then enters suspend-to-RAM. If the system wakes up from suspend it will discard the hibernation image, but if the system loses power it can safely restore from the hibernation image.

  1. The difference between soft and mechanical off is that mechanical off is “entered and left by a mechanical means (for example, turning off the system’s power through the movement of a large red switch)” 

  2. It’s unclear to me why G and S states overlap like this. I assume this is a relic of an older spec that only had S states, but I have not as yet found any evidence of this. If someone has any information on this, please let me know and I’ll update this footnote. 

  3. Of the operating systems I know of that support ACPI sleep states (I checked Windows, Mac, Linux, and the three BSDs8), only MacOS does not allow the user to deliberately enable hibernation, instead supporting a hybrid suspend it calls safe sleep 

  4. “The reset vector of a processor is the default location where, upon a reset, the processor will go to find the first instruction to execute. In other words, the reset vector is a pointer or address where the processor should always begin its execution. This first instruction typically branches to the system initialization code.” Xiaocong Fan, Real-Time Embedded Systems, 2015 

  5. All kernel threads are tagged with PF_NOFREEZE by default, so they must specifically opt-in to task freezing. 

  6. This is not from the docs, but from kernel/freezer.c which also notes “Refrigerator is place where frozen processes are stored :-).” 

  7. This is the operation that requires “special architecture-specific low-level code”. 

  8. Interestingly NetBSD has a setting to enable hibernation, but does not actually support hibernation 

Categories: FLOSS Project Planets

Armin Ronacher: Extension Maps in Rust

Planet Python - Wed, 2022-01-05 19:00

Sometimes in Rust you want to design APIs that provide a little bit of flexibility for the user. A common approach for this is to introduce a generic type parameter that can be filled in. Let's for instance think of a web application framework. It might have an application type which is passed to all kinds of functions. That application type might want to be parametrized over the configuration of the application:

struct App<Config> { config: Config }

While this works, it quickly becomes complex. What if in addition to a config you also want to have custom state there? It also means all functions that want to work with that app need to be generic over the config as well. Worse: what if you don't even know which parametrizations are needed?

Enter The Any Type

A solution for this issue comes in the form of the Any type. It lets you take a 'static type and box it up as a dyn Any so you can do something with it later. That later is for instance can involve a downcast to the original type. This means you can store arbitrary types somewhere (like on our App object) and retrieve them later.

So we basically want something like the following API wise:

let app = App::new(); // place in extension map app.extensions().insert(MyConfig { ... }); app.extensions().insert(MyDatabase { ... }); // retrieve from extension map let config = app.extensions().get::<Config>();

So we basically want a type which can hold such extensions so we can use it later. For now let's look at the most simplest of these options: an Extensions object which lets you retrieve extensions and insert new ones. If an extension does not exist yet, instead of panicking we will automatically upsert it (eg: the type needs to implement Default):

use std::collections::HashMap; use std::any::{Any, TypeId}; #[derive(Default)] pub struct Extensions { map: HashMap<TypeId, Box<dyn Any>>, } impl Extensions { pub fn insert<T: 'static>(&mut self, value: T) { self.map.insert(TypeId::of::<T>(), Box::new(value)); } pub fn get<T: Default + 'static>(&self) -> &T { self.ensure::<T>(); self.map.get(&TypeId::of::<T>()) .and_then(|b| b.downcast_ref()) .unwrap() } pub fn get_mut<T: Default + 'static>(&mut self) -> &mut T { self.ensure::<T>(); self.map.get_mut(&TypeId::of::<T>()) .and_then(|b| b.downcast_mut()) .unwrap() } fn ensure<T: Default + 'static>(&self) { if self.map.get(&TypeId::of::<T>()).is_none() { self.insert(T::default()); } } }

So this is pretty straightforward to use now but leaves one issue behind: the borrow checker will make your life quite hard. The above map is quite useful for the typical setup where you have an object like an application, you configure it once, and from then on the extension map is frozen as there are too many shared references to it flying around that nobody will ever be able to get a &mut reference to it any more.

So how does this work? Basically each rust type has a type ID which you can retrieve with TypeId::of::<T>(). It's a unique value that you can use for comparisons or as a key in a map which is what we're doing here. Of each type one value is permitted. We then store this in the map as dyn Any which lets us use the downcast_ref and downcast_mut method to case the value back to what we had originally. We know that these casts won't fail in our case so we can safely unwrap() them.

But what if you need to have some sort of interior mutability?

Interior Mutability Extension Map

Let's look at a common case of a web framework or template engine. Take the MiniJinja template engine for instance. It has a State object which is created once per template initialization, is not Send or Sync and holds state the engine needs for the evaluation. What if you want to make it possible for a user to put their own state on it? In that case one can adapt the type from above by using RefCell internally:

use std::collections::HashMap; use std::any::{Any, TypeId}; use std::cell::{Ref, RefCell, RefMut}; #[derive(Default)] pub struct Extensions { map: RefCell<HashMap<TypeId, Box<dyn Any>>>, } impl Extensions { pub fn insert<T: 'static>(&self, value: T) { self.map.borrow_mut().insert(TypeId::of::<T>(), Box::new(value)); } pub fn get<T: Default + 'static>(&self) -> Ref<'_, T> { self.ensure::<T>(); Ref::map(self.map.borrow(), |m| { m.get(&TypeId::of::<T>()) .and_then(|b| b.downcast_ref()) .unwrap() }) } pub fn get_mut<T: Default + 'static>(&self) -> RefMut<'_, T> { self.ensure::<T>(); RefMut::map(self.map.borrow_mut(), |m| { m.get_mut(&TypeId::of::<T>()) .and_then(|b| b.downcast_mut()) .unwrap() }) } fn ensure<T: Default + 'static>(&self) { if self.map.borrow().get(&TypeId::of::<T>()).is_none() { self.insert(T::default()); } } }

From the end user's perspective not much has changed. The main difference is now that yo can call get_mut even if you do not have a mutable reference to the extension map. This feat is accomplished by RefCell having the ability to move the necessary checks to runtime. When a RefMut is given out Rust will panic if there are any shared loans out or already another mutable reference. For the users here this is not much of a concern as we can easily ensure that there is only ever one mutable reference in use. What makes RefCell particularly great here is that the Ref and RefMut types have a static map method that lets you derive another Ref or RefMut that holds on to the original loan, but transforms the value.

Going Sync

Alright. But what if we want to do the same trick as above but with Send and Sync? Well in that case we need a locking type. Sadly the Mutex or RwLock from the standard library does not provide a way to hold on to the loan and map it, so we need to use something else. You can use the parking_lot crate instead which provides the necessary functionality:

use parking_lot::{ MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard, RwLockWriteGuard, }; use std::any::{Any, TypeId}; use std::collections::HashMap; #[derive(Default)] pub struct Extensions { map: RwLock<HashMap<TypeId, Box<dyn Any>>>, } impl Extensions { pub fn insert<T: 'static>(&self, value: T) { self.map.write().insert(TypeId::of::<T>(), Box::new(value)); } pub fn get<T: Default + 'static>(&self) -> MappedRwLockReadGuard<'_, T> { self.ensure::<T>(); RwLockReadGuard::map(self.map.read(), |m| { m.get(&TypeId::of::<T>()) .and_then(|b| b.downcast_ref()) .unwrap() }) } pub fn get_mut<T: Default + 'static>(&self) -> MappedRwLockWriteGuard<'_, T> { self.ensure::<T>(); RwLockWriteGuard::map(self.map.write(), |m| { m.get_mut(&TypeId::of::<T>()) .and_then(|b| b.downcast_mut()) .unwrap() }) } fn ensure<T: Default + 'static>(&self) { if self.map.read().get(&TypeId::of::<T>()).is_none() { self.insert(T::default()); } } }

Happy extending!

Categories: FLOSS Project Planets

myDropWizard.com: Drupal 6 security update for Wysiwyg module

Planet Drupal - Wed, 2022-01-05 18:44

As you may know, Drupal 6 has reached End-of-Life (EOL) which means the Drupal Security Team is no longer doing Security Advisories or working on security patches for Drupal 6 core or contrib modules - but the Drupal 6 LTS vendors are and we're one of them!

Today, there is a Moderately Critical security release for the Wysiwyg module to fix a Cross Site Scripting (XSS) vulnerability.

The Wysiwyg module provides one way to integrate various WYSIWYG editors into Drupal.

See the security advisory for Drupal 7 for more information.

Here you can download the Drupal 6 patch or the full release.

If you have a Drupal 6 site using the Wysiwyg module, we recommend you update immediately! We have already deployed the patch for all of our Drupal 6 Long-Term Support clients. :-)

Note: if you use the myDropWizard module (totally free!), you'll be alerted to these and any future security updates, and will be able to use drush to install them (even though they won't necessarily have a release on Drupal.org).

Categories: FLOSS Project Planets

TestDriven.io: Pagination in Django

Planet Python - Wed, 2022-01-05 17:28
This article looks at how to add pagination to a Django project.
Categories: FLOSS Project Planets

parallel @ Savannah: GNU Parallel's 20th birthday

GNU Planet! - Wed, 2022-01-05 17:19

On 2022-01-06 GNU Parallel will be 20 years old. The birthday is an opportunity to take stock.

https://www.gnu.org/software/parallel/20th-birthday.html

Categories: FLOSS Project Planets

Jacob Rockowitz: Part 5: Good Drupal Leadership: What are the relationships an organization needs to build to succeed with Drupal?

Planet Drupal - Wed, 2022-01-05 16:16

Drupal’s entire existence is rooted in the fact that it is a collaboration of organizations and individuals, and that massive collaboration requires communication and relationships. Minimally, if an organization wants a secure website/application, it must connect with the Drupal community to be notified about security releases. Very few enterprise implementations of Drupal can be accomplished by a single, independent group. Websites and their digital experiences are too complex. Organizations frequently separate development from hosting. With decoupled implementations of Drupal, organizations are beginning to unravel the front-end from the backend.

The complexities and challenges of Drupal websites/applications are endless. Having good partners and hiring the right vendors can help strengthen an organization's planning and process. For example, SaaS hosting providers specializing in Drupal can easily be responsible for most of an organization's infrastructure-related tasks.In my previous posts about good Drupal leadership, I discussed the challenges of planning, implementing, and resourcing. To help establish good Drupal leadership, organizations need to understand the type of relationships involved and what is required to strengthen these relationships.

Relationships types

Internal

Anyone not directly involved with Drupal will want to know, "What is Drupal?" An organization with an executive-level summary answering the “what” and...Read More

Categories: FLOSS Project Planets

Python Software Foundation: Announcing Python Software Foundation Fellow Members for Q4 2021! 🎉

Planet Python - Wed, 2022-01-05 13:26

The PSF is pleased to announce its fourth batch of PSF Fellows for 2021! Let us welcome the new PSF Fellows for Q4! The following people continue to do amazing things for the Python community:

Ana Dulce Padovan

TwitterGitHub

Marcelo Elizeche Landó

LinkedInWebsiteGitHub

Sarah Kaiser

LinkedInWebsiteTwitterGitHub

Thank you for your continued contributions. We have added you to our Fellow roster online.

The above members help support the Python ecosystem by being phenomenal leaders, sustaining the growth of the Python scientific community, maintaining virtual Python communities, maintaining Python libraries, creating educational material, organizing Python events and conferences, starting Python communities in local regions, and overall being great mentors in our community. Each of them continues to help make Python more accessible around the world. To learn more about the new Fellow members, check out their links above.

Let's continue recognizing Pythonistas all over the world for their impact on our community. The criteria for Fellow members is available online: https://www.python.org/psf/fellows/. If you would like to nominate someone to be a PSF Fellow, please send a description of their Python accomplishments and their email address to psf-fellow at python.org. We are accepting nominations for quarter 1 through March 3, 2022.

Are you a PSF Fellow and want to help the Work Group review nominations? Contact us at psf-fellow at python.org.

Categories: FLOSS Project Planets

Andre Roberge: Python 101: enabling a restricted subset of Python

Planet Python - Wed, 2022-01-05 13:06

I decided to submit to the the Ideas category of Discuss-Python a proposal which I have summarized as follows:

Summary: I propose that a new compile time directive be available to restrict the Python syntax to a strict subset. This would facilitate the teaching of Python to beginners as well as the work of people that write tools intended to help beginners learning Python.

Here is a link to that post.

This is something I have been thinking about for more than a year but always hesitated to submit. It is now done ... feel free to comment over there.

Comments posted on this blog about this particular topic will be deleted so that the discussion can take place at a single location.

Categories: FLOSS Project Planets

Pages